Explorar o código

Adding the rest of the preflight checks (#2828)

Feroze Mohideen %!s(int64=3) %!d(string=hai) anos
pai
achega
b1e2b80f30

+ 7 - 0
api/server/handlers/project_integration/preflight_check_aws_usage.go

@@ -2,6 +2,7 @@ package project_integration
 
 import (
 	"fmt"
+	"log"
 	"net/http"
 
 	"github.com/bufbuild/connect-go"
@@ -32,11 +33,17 @@ func (p *CreatePreflightCheckAWSUsageHandler) ServeHTTP(w http.ResponseWriter, r
 	project, _ := r.Context().Value(types.ProjectScope).(*models.Project)
 	ctx := r.Context()
 
+	log.Println("got here")
+
 	request := &types.QuotaPreflightCheckRequest{}
 	if ok := p.DecodeAndValidate(w, r, request); !ok {
 		return
 	}
 
+	log.Println("project id: ", project.ID)
+	log.Println("target arn: ", request.TargetARN)
+	log.Println("region: ", request.Region)
+
 	checkReq := porterv1.QuotaPreflightCheckRequest{
 		ProjectId: int64(project.ID),
 		TargetArn: request.TargetARN,

+ 1 - 1
dashboard/src/assets/arrow-down.svg

@@ -1,3 +1,3 @@
 <svg width="16" height="10" viewBox="0 0 16 10" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M15 1.5L8 8.5L1 1.5" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M15 1.5L8 8.5L1 1.5" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
 </svg>

+ 4 - 4
dashboard/src/assets/document.svg

@@ -1,6 +1,6 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M15.7162 16.2234H8.49622" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M15.7162 12.0369H8.49622" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M11.2513 7.86011H8.49631" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M15.9086 2.74982C15.9086 2.74982 8.23161 2.75382 8.21961 2.75382C5.45961 2.77082 3.75061 4.58682 3.75061 7.35682V16.5528C3.75061 19.3368 5.47261 21.1598 8.25661 21.1598C8.25661 21.1598 15.9326 21.1568 15.9456 21.1568C18.7056 21.1398 20.4156 19.3228 20.4156 16.5528V7.35682C20.4156 4.57282 18.6926 2.74982 15.9086 2.74982Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M15.7162 16.2234H8.49622" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M15.7162 12.0369H8.49622" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M11.2513 7.86011H8.49631" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M15.9086 2.74982C15.9086 2.74982 8.23161 2.75382 8.21961 2.75382C5.45961 2.77082 3.75061 4.58682 3.75061 7.35682V16.5528C3.75061 19.3368 5.47261 21.1598 8.25661 21.1598C8.25661 21.1598 15.9326 21.1568 15.9456 21.1568C18.7056 21.1398 20.4156 19.3228 20.4156 16.5528V7.35682C20.4156 4.57282 18.6926 2.74982 15.9086 2.74982Z" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
 </svg>

+ 2 - 2
dashboard/src/assets/down-arrow.svg

@@ -1,4 +1,4 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M12.2743 19.75V4.75" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M18.2987 13.7002L12.2747 19.7502L6.24969 13.7002" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M12.2743 19.75V4.75" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M18.2987 13.7002L12.2747 19.7502L6.24969 13.7002" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
 </svg>

+ 1 - 1
dashboard/src/assets/filter-outline.svg

@@ -1,3 +1,3 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M9.29332 22L14.0696 19.7519V13.8603L21.5593 6.26456C21.8416 5.97995 22 5.58933 22 5.18027V3.51754C22 2.67869 21.3417 2 20.5295 2H3.47049C2.65826 2 2 2.67869 2 3.51754V5.2183C2 5.60431 2.14169 5.97534 2.39719 6.2565L9.29332 13.8603V22Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.29332 22L14.0696 19.7519V13.8603L21.5593 6.26456C21.8416 5.97995 22 5.58933 22 5.18027V3.51754C22 2.67869 21.3417 2 20.5295 2H3.47049C2.65826 2 2 2.67869 2 3.51754V5.2183C2 5.60431 2.14169 5.97534 2.39719 6.2565L9.29332 13.8603V22Z" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
 </svg>

+ 2 - 2
dashboard/src/assets/folder-outline.svg

@@ -1,4 +1,4 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M21.4446 15.7579C21.4446 19.336 19.336 21.4446 15.7579 21.4446H7.97172C4.38443 21.4446 2.27588 19.336 2.27588 15.7579V7.9626C2.27588 4.38444 3.5903 2.27588 7.16846 2.27588H9.16749C9.88576 2.27588 10.5621 2.61406 10.9931 3.18868L11.9059 4.40269C12.3378 4.97618 13.0135 5.31406 13.7315 5.31549H16.5611C20.1484 5.31549 21.472 7.14108 21.472 10.7923L21.4446 15.7579Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M7.05893 14.4891H16.6524" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M21.4446 15.7579C21.4446 19.336 19.336 21.4446 15.7579 21.4446H7.97172C4.38443 21.4446 2.27588 19.336 2.27588 15.7579V7.9626C2.27588 4.38444 3.5903 2.27588 7.16846 2.27588H9.16749C9.88576 2.27588 10.5621 2.61406 10.9931 3.18868L11.9059 4.40269C12.3378 4.97618 13.0135 5.31406 13.7315 5.31549H16.5611C20.1484 5.31549 21.472 7.14108 21.472 10.7923L21.4446 15.7579Z" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M7.05893 14.4891H16.6524" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
 </svg>

+ 3 - 3
dashboard/src/assets/info-circle.svg

@@ -1,5 +1,5 @@
 <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M9.99988 0.750183C15.1089 0.750183 19.2499 4.89218 19.2499 10.0002C19.2499 15.1082 15.1089 19.2502 9.99988 19.2502C4.89188 19.2502 0.749878 15.1082 0.749878 10.0002C0.749878 4.89218 4.89188 0.750183 9.99988 0.750183Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M9.995 6.20428V10.6233" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M9.995 13.7961H10.005" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.99988 0.750183C15.1089 0.750183 19.2499 4.89218 19.2499 10.0002C19.2499 15.1082 15.1089 19.2502 9.99988 19.2502C4.89188 19.2502 0.749878 15.1082 0.749878 10.0002C0.749878 4.89218 4.89188 0.750183 9.99988 0.750183Z" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M9.995 6.20428V10.6233" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M9.995 13.7961H10.005" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
 </svg>

+ 3 - 3
dashboard/src/assets/info-outlined.svg

@@ -1,5 +1,5 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M11.9899 15.7961V11.3771" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M11.9899 8.20428H11.9999" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M16.3346 2.75018H7.66561C4.64461 2.75018 2.75061 4.88918 2.75061 7.91618V16.0842C2.75061 19.1112 4.63561 21.2502 7.66561 21.2502H16.3336C19.3646 21.2502 21.2506 19.1112 21.2506 16.0842V7.91618C21.2506 4.88918 19.3646 2.75018 16.3346 2.75018Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M11.9899 15.7961V11.3771" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M11.9899 8.20428H11.9999" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16.3346 2.75018H7.66561C4.64461 2.75018 2.75061 4.88918 2.75061 7.91618V16.0842C2.75061 19.1112 4.63561 21.2502 7.66561 21.2502H16.3336C19.3646 21.2502 21.2506 19.1112 21.2506 16.0842V7.91618C21.2506 4.88918 19.3646 2.75018 16.3346 2.75018Z" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
 </svg>

+ 2 - 2
dashboard/src/assets/last-run.svg

@@ -1,4 +1,4 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M11.3002 12.2513L20.2502 12.2513" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3002 7.25031L3.36317 12.2513L11.3002 17.2523L11.3002 7.25031Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M11.3002 12.2513L20.2502 12.2513" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3002 7.25031L3.36317 12.2513L11.3002 17.2523L11.3002 7.25031Z" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
 </svg>

+ 2 - 2
dashboard/src/assets/left-arrow.svg

@@ -1,4 +1,4 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M4.25 12.2743L19.25 12.2743" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M10.2998 18.2987L4.2498 12.2747L10.2998 6.24969" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M4.25 12.2743L19.25 12.2743" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M10.2998 18.2987L4.2498 12.2747L10.2998 6.24969" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
 </svg>

+ 4 - 4
dashboard/src/assets/sort.svg

@@ -1,6 +1,6 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M16.8396 20.1642V6.54645" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M20.9172 16.0681L16.8394 20.1648L12.7617 16.0681" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M6.91112 3.83289V17.4507" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M2.83344 7.929L6.91121 3.83234L10.989 7.929" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M16.8396 20.1642V6.54645" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M20.9172 16.0681L16.8394 20.1648L12.7617 16.0681" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M6.91112 3.83289V17.4507" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M2.83344 7.929L6.91121 3.83234L10.989 7.929" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
 </svg>

+ 4 - 4
dashboard/src/assets/tag.svg

@@ -1,6 +1,6 @@
 <svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M14.8055 18.9994V3" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M7.19465 3.00064V19" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M3.00065 14.8054L19 14.8054" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M18.9994 7.19465L3.00004 7.19465" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M14.8055 18.9994V3" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M7.19465 3.00064V19" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M3.00065 14.8054L19 14.8054" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M18.9994 7.19465L3.00004 7.19465" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
 </svg>

+ 2 - 2
dashboard/src/assets/time.svg

@@ -1,4 +1,4 @@
 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M21.2498 12.0005C21.2498 17.1095 17.1088 21.2505 11.9998 21.2505C6.8908 21.2505 2.7498 17.1095 2.7498 12.0005C2.7498 6.89149 6.8908 2.75049 11.9998 2.75049C17.1088 2.75049 21.2498 6.89149 21.2498 12.0005Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M15.4314 14.9429L11.6614 12.6939V7.84686" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M21.2498 12.0005C21.2498 17.1095 17.1088 21.2505 11.9998 21.2505C6.8908 21.2505 2.7498 17.1095 2.7498 12.0005C2.7498 6.89149 6.8908 2.75049 11.9998 2.75049C17.1088 2.75049 21.2498 6.89149 21.2498 12.0005Z" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
+<path d="M15.4314 14.9429L11.6614 12.6939V7.84686" stroke="white" stroke-width="1.5" stroke-linecap="round" strokeLinejoin="round"/>
 </svg>

+ 6 - 4
dashboard/src/components/CloudFormationForm.tsx

@@ -90,7 +90,7 @@ const CloudFormationForm: React.FC<Props> = ({
           </Text>
           <Spacer height="15px" />
           <Text color="helper">
-            Provide your AWS account ID to log in and grant Porter access to AWS. You will need to select "Create stack" after being redirected to the AWS console below.
+            Provide your AWS account ID to log in and grant Porter access to AWS. You will need to select "Create stack" after being redirected to the AWS console below. Make sure that the stack status has changed from "CREATE_IN_PROGRESS" to "CREATE_COMPLETE" before clicking Continue.
           </Text>
           <Spacer y={1} />
           <Input
@@ -110,14 +110,14 @@ const CloudFormationForm: React.FC<Props> = ({
             value={AWSAccountID}
             setValue={(e) => {
               setGrantPermissionsError("");
-              setAWSAccountID(e);
+              setAWSAccountID(e.trim());
             }}
             placeholder="ex: 915037676314"
           />
           <Spacer y={1} />
           <Button
             onClick={() => {
-              if (AWSAccountID.length === 12) {
+              if (AWSAccountID.length === 12 && !isNaN(Number(AWSAccountID))) {
                 directToCloudFormation();
               } else {
                 setGrantPermissionsError("Invalid AWS account ID");
@@ -172,7 +172,9 @@ const CloudFormationForm: React.FC<Props> = ({
                     <Spacer y={1} />
                     <Step number={4}>After being redirected to AWS, select "Create stack" on the AWS console.</Step>
                     <Spacer y={1} />
-                    <Step number={5}>Return to Porter and select "Continue".</Step>
+                    <Step number={5}>Wait until the stack status has changed from "CREATE_IN_PROGRESS" to "CREATE_COMPLETE".</Step>
+                    <Spacer y={1} />
+                    <Step number={6}>Return to Porter and select "Continue".</Step>
                   </>
                 }
               />

+ 200 - 6
dashboard/src/components/ProvisionerSettings.tsx

@@ -11,12 +11,14 @@ import SelectRow from "components/form-components/SelectRow";
 import Heading from "components/form-components/Heading";
 import Helper from "components/form-components/Helper";
 import InputRow from "./form-components/InputRow";
-import SaveButton from "./SaveButton";
 import { Contract, EnumKubernetesKind, EnumCloudProvider, NodeGroupType, EKSNodeGroup, EKS, Cluster } from "@porter-dev/api-contracts";
 import { ClusterType } from "shared/types";
 import Button from "./porter/Button";
-import Text from "./porter/Text";
+import Error from "./porter/Error";
 import Spacer from "./porter/Spacer";
+import Step from "./porter/Step";
+import Link from "./porter/Link";
+import Text from "./porter/Text";
 
 const regionOptions = [
   { value: "us-east-1", label: "US East (N. Virginia) us-east-1" },
@@ -92,6 +94,19 @@ const ProvisionerSettings: React.FC<Props> = props => {
     }
   }
 
+  const getStatus = () => {
+    if (isReadOnly) {
+      return "Provisioning is still in progress..."
+    } else if (errorMessage) {
+      return <Error
+        message={errorMessage}
+        ctaText={errorMessage !== DEFAULT_ERROR_MESSAGE ? "Troubleshooting steps" : null}
+        errorModalContents={errorMessageToModal(errorMessage)}
+      />;
+    }
+    return undefined;
+  }
+
   const createCluster = async () => {
     markProvisioningStarted();
 
@@ -187,7 +202,21 @@ const ProvisionerSettings: React.FC<Props> = props => {
       }
       setErrorMessage(undefined);
     } catch (err) {
-      setErrorMessage(err.response.data.error.replace('unknown: ', ''));
+      const errMessage = err.response.data.error.replace('unknown: ', '');
+      // hacky, need to standardize error contract with backend
+      if (errMessage.includes('elastic IP')) {
+        setErrorMessage(AWS_EIP_QUOTA_ERROR_MESSAGE)
+      } else if (errMessage.includes('VPC')) {
+        setErrorMessage(AWS_VPC_QUOTA_ERROR_MESSAGE)
+      } else if (errMessage.includes('NAT Gateway')) {
+        setErrorMessage(AWS_NAT_GATEWAY_QUOTA_ERROR_MESSAGE)
+      } else if (errMessage.includes('vCPU')) {
+        setErrorMessage(AWS_VCPU_QUOTA_ERROR_MESSAGE)
+      } else if (errMessage.includes('AWS account')) {
+        setErrorMessage(AWS_LOGIN_ERROR_MESSAGE)
+      } else {
+        setErrorMessage(DEFAULT_ERROR_MESSAGE)
+      }
     } finally {
       setIsReadOnly(false)
     }
@@ -330,9 +359,8 @@ const ProvisionerSettings: React.FC<Props> = props => {
       <Button
         disabled={(!clusterName && true) || isReadOnly}
         onClick={createCluster}
-        status={isReadOnly && "Provisioning is still in progress"}
+        status={getStatus()}
       >Provision</Button>
-      {errorMessage && <ErrorContainer>{errorMessage} Please correct the issue and try to provision again.</ErrorContainer>}
     </>
   );
 };
@@ -370,4 +398,170 @@ const ErrorContainer = styled.div`
   font-size: 13px;
   margin-bottom: 30px;
   color: red;
-`
+`
+
+const AWS_LOGIN_ERROR_MESSAGE = "Porter could not access your AWS account. Please make sure you have granted permissions and try again."
+const AWS_EIP_QUOTA_ERROR_MESSAGE = "Your AWS account has reached the limit of elastic IPs allowed in the region. Additional addresses must be requested in order to provision."
+const AWS_VPC_QUOTA_ERROR_MESSAGE = "Your AWS account has reached the limit of VPCs allowed in the region. Additional VPCs must be requested in order to provision."
+const AWS_NAT_GATEWAY_QUOTA_ERROR_MESSAGE = "Your AWS account has reached the limit of NAT Gateways allowed in the region. Additional NAT Gateways must be requested in order to provision."
+const AWS_VCPU_QUOTA_ERROR_MESSAGE = "Your AWS account has reached the limit of vCPUs allowed in the region. Additional vCPUs must be requested in order to provision."
+const DEFAULT_ERROR_MESSAGE = "An error occurred while provisioning your infrastructure. Please try again."
+
+const errorMessageToModal = (errorMessage: string) => {
+  switch (errorMessage) {
+    case AWS_LOGIN_ERROR_MESSAGE:
+      return (
+        <>
+          <Text size={16} weight={500}>Granting Porter access to AWS</Text>
+          <Spacer y={1} />
+          <Text color="helper">
+            Porter needs access to your AWS account in order to create infrastructure. You can grant Porter access to AWS by following these steps:
+          </Text>
+          <Spacer y={1} />
+          <Step number={1}>
+            <Link to="https://aws.amazon.com/resources/create-account/" target="_blank">
+              Create an AWS account
+            </Link>
+            <Spacer inline width="5px" />
+            if you don't already have one.
+          </Step>
+          <Spacer y={1} />
+          <Step number={2}>
+            Once you are logged in to your AWS account,
+            <Spacer inline width="5px" />
+            <Link to="https://console.aws.amazon.com/billing/home?region=us-east-1#/account" target="_blank">
+              copy your account ID
+            </Link>.
+          </Step>
+          <Spacer y={1} />
+          <Step number={3}>Fill in your account ID on Porter and select "Grant permissions".</Step>
+          <Spacer y={1} />
+          <Step number={4}>After being redirected to AWS, select "Create stack" on the AWS console.</Step>
+          <Spacer y={1} />
+          <Step number={5}>Return to Porter and select "Continue".</Step>
+        </>
+      )
+    case AWS_EIP_QUOTA_ERROR_MESSAGE:
+      return (
+        <>
+          <Text size={16} weight={500}>Requesting more EIP Adresses</Text>
+          <Spacer y={1} />
+          <Text color="helper">
+            You will need to either request more EIP addresses or delete existing ones in order to provision in the region specified. You can request more addresses by following these steps:
+          </Text>
+          <Spacer y={1} />
+          <Step number={1}>
+            Log into
+            <Spacer inline width="5px" />
+            <Link to="https://console.aws.amazon.com/billing/home?region=us-east-1#/account" target="_blank">your AWS account
+            </Link>.
+          </Step>
+          <Spacer y={1} />
+          <Step number={2}>
+            Navigate to
+            <Spacer inline width="5px" />
+            <Link to="https://us-east-1.console.aws.amazon.com/servicequotas/home/services/ec2/quotas" target="_blank">the Amazon Elastic Compute Cloud (Amazon EC2) Service Quotas portal
+            </Link>.
+          </Step>
+          <Spacer y={1} />
+          <Step number={3}>Search for "EC2-VPC Elastic IPs" in the search box and click on the search result.</Step>
+          <Spacer y={1} />
+          <Step number={4}>Click on "Request quota increase". In order to provision with Porter, you will need to request at least 3 addresses above your current quota limit.</Step>
+          <Spacer y={1} />
+          <Step number={5}>Once that request is approved, return to Porter and retry the provision.</Step>
+        </>
+      )
+    case AWS_VPC_QUOTA_ERROR_MESSAGE:
+      return (
+        <>
+          <Text size={16} weight={500}>Requesting more VPCs</Text>
+          <Spacer y={1} />
+          <Text color="helper">
+            You will need to either request more VPCs or delete existing ones in order to provision in the region specified. You can request more VPCs by following these steps:
+          </Text>
+          <Spacer y={1} />
+          <Step number={1}>
+            Log into
+            <Spacer inline width="5px" />
+            <Link to="https://console.aws.amazon.com/billing/home?region=us-east-1#/account" target="_blank">your AWS account
+            </Link>.
+          </Step>
+          <Spacer y={1} />
+          <Step number={2}>
+            Navigate to
+            <Spacer inline width="5px" />
+            <Link to="https://us-east-1.console.aws.amazon.com/servicequotas/home/services/vpc/quotas" target="_blank">the Amazon Virtual Private Cloud (Amazon VPC) Service Quotas portal
+            </Link>.
+          </Step>
+          <Spacer y={1} />
+          <Step number={3}>Search for "VPCs per Region" in the search box and click on the search result.</Step>
+          <Spacer y={1} />
+          <Step number={4}>Click on "Request quota increase". In order to provision with Porter, you will need to request at least 1 VPCs above your current quota limit.</Step>
+          <Spacer y={1} />
+          <Step number={5}>Once that request is approved, return to Porter and retry the provision.</Step>
+        </>
+      )
+    case AWS_NAT_GATEWAY_QUOTA_ERROR_MESSAGE:
+      return (
+        <>
+          <Text size={16} weight={500}>Requesting more NAT Gateways</Text>
+          <Spacer y={1} />
+          <Text color="helper">
+            You will need to either request more NAT Gateways or delete existing ones in order to provision in the region specified. You can request more NAT Gateways by following these steps:
+          </Text>
+          <Spacer y={1} />
+          <Step number={1}>
+            Log into
+            <Spacer inline width="5px" />
+            <Link to="https://console.aws.amazon.com/billing/home?region=us-east-1#/account" target="_blank">your AWS account
+            </Link>.
+          </Step>
+          <Spacer y={1} />
+          <Step number={2}>
+            Navigate to
+            <Spacer inline width="5px" />
+            <Link to="https://us-east-1.console.aws.amazon.com/servicequotas/home/services/vpc/quotas" target="_blank">the Amazon Virtual Private Cloud (Amazon VPC) Service Quotas portal
+            </Link>.
+          </Step>
+          <Spacer y={1} />
+          <Step number={3}>Search for "NAT gateways per Availability Zone" in the search box and click on the search result.</Step>
+          <Spacer y={1} />
+          <Step number={4}>Click on "Request quota increase". In order to provision with Porter, you will need to request at least 3 NAT Gateways above your current quota limit.</Step>
+          <Spacer y={1} />
+          <Step number={5}>Once that request is approved, return to Porter and retry the provision.</Step>
+        </>
+      )
+    case AWS_VCPU_QUOTA_ERROR_MESSAGE:
+      return (
+        <>
+          <Text size={16} weight={500}>Requesting more vCPUs</Text>
+          <Spacer y={1} />
+          <Text color="helper">
+            You will need to either request more vCPUs or delete existing instances in order to provision in the region specified. You can request more vCPUs by following these steps:
+          </Text>
+          <Spacer y={1} />
+          <Step number={1}>
+            Log into
+            <Spacer inline width="5px" />
+            <Link to="https://console.aws.amazon.com/billing/home?region=us-east-1#/account" target="_blank">your AWS account
+            </Link>.
+          </Step>
+          <Spacer y={1} />
+          <Step number={2}>
+            Navigate to
+            <Spacer inline width="5px" />
+            <Link to="https://us-east-1.console.aws.amazon.com/servicequotas/home/services/ec2/quotas" target="_blank">the Amazon Elastic Compute Cloud (Amazon EC2) Service Quotas portal
+            </Link>.
+          </Step>
+          <Spacer y={1} />
+          <Step number={3}>Search for "Running On-Demand Standard (A, C, D, H, I, M, R, T, Z) instances" in the search box and click on the search result.</Step>
+          <Spacer y={1} />
+          <Step number={4}>Click on "Request quota increase". In order to provision with Porter, you will need to request at least 10 vCPUs above your current quota limit.</Step>
+          <Spacer y={1} />
+          <Step number={5}>Once that request is approved, return to Porter and retry the provision.</Step>
+        </>
+      )
+    default:
+      return null
+  }
+}

+ 2 - 0
dashboard/src/components/porter/Link.tsx

@@ -31,9 +31,11 @@ export default Link;
 const Div = styled.span`
   color: #8590ff;
   cursor: pointer;
+  display: inline;
 `;
 
 const StyledLink = styled(DynamicLink)`
   color: #8590ff;
+  display: inline;
   cursor: pointer;
 `;

+ 3 - 0
dashboard/src/components/porter/Spacer.tsx

@@ -34,6 +34,7 @@ const Spacer: React.FC<Props> = ({
     <StyledSpacer
       height={height || getCalcHeight()}
       width={inline && (width || getCalcWidth())}
+      inline={inline}
     />
   );
 };
@@ -43,7 +44,9 @@ export default Spacer;
 const StyledSpacer = styled.div<{ 
   height: string;
   width: string;
+  inline: boolean;
 }>`
+  display: ${props => props.inline ? "inline-block" : "block"};
   height: ${props => props.height || "100%"};
   width: ${props => props.height ? "100%" : props.width};
 `;

+ 7 - 1
dashboard/src/components/porter/Step.tsx

@@ -13,13 +13,19 @@ const Step: React.FC<Props> = ({
   return (
     <StyledStep>
       <StepNumber>{number}</StepNumber>
-      {children}
+      <Block>
+        {children}
+      </Block>
     </StyledStep>
   );
 };
 
 export default Step;
 
+const Block = styled.div`
+  display: block;
+`;
+
 const StepNumber = styled.div`
   height: 20px;
   min-width: 20px;

+ 60 - 60
dashboard/src/main/home/cluster-dashboard/dashboard/ClusterSettingsModal.tsx

@@ -14,9 +14,9 @@ type Props = {
 
 const ClusterSettingsModal: React.FC<Props> = ({
 }) => {
-  const { 
-    setCurrentModal, 
-    currentCluster, 
+  const {
+    setCurrentModal,
+    currentCluster,
     currentProject,
     setShouldRefreshClusters,
   } = useContext(Context);
@@ -56,71 +56,71 @@ const ClusterSettingsModal: React.FC<Props> = ({
       <Spacer height="15px" />
       <Flex>
         <IconWrapper>
-        <svg
-          width="18"
-          height="18"
-          viewBox="0 0 19 19"
-          fill="none"
-          xmlns="http://www.w3.org/2000/svg"
-        >
-          <path
-            d="M15.207 12.4403C16.8094 12.4403 18.1092 11.1414 18.1092 9.53907C18.1092 7.93673 16.8094 6.63782 15.207 6.63782"
-            stroke="white"
-            strokeWidth="1.5"
-            strokeLinecap="round"
-            stroke-linejoin="round"
-          />
-          <path
-            d="M3.90217 12.4403C2.29983 12.4403 1 11.1414 1 9.53907C1 7.93673 2.29983 6.63782 3.90217 6.63782"
-            stroke="white"
-            strokeWidth="1.5"
-            strokeLinecap="round"
-            stroke-linejoin="round"
-          />
-          <path
-            fillRule="evenodd"
-            clipRule="evenodd"
-            d="M9.54993 13.4133C7.4086 13.4133 5.69168 11.6964 5.69168 9.55417C5.69168 7.41284 7.4086 5.69592 9.54993 5.69592C11.6913 5.69592 13.4082 7.41284 13.4082 9.55417C13.4082 11.6964 11.6913 13.4133 9.54993 13.4133Z"
-            stroke="white"
-            strokeWidth="1.5"
-            strokeLinecap="round"
-            stroke-linejoin="round"
-          />
-          <path
-            d="M6.66895 15.207C6.66895 16.8094 7.96787 18.1092 9.5702 18.1092C11.1725 18.1092 12.4715 16.8094 12.4715 15.207"
-            stroke="white"
-            strokeWidth="1.5"
-            strokeLinecap="round"
-            stroke-linejoin="round"
-          />
-          <path
-            d="M6.66895 3.90217C6.66895 2.29983 7.96787 1 9.5702 1C11.1725 1 12.4715 2.29983 12.4715 3.90217"
-            stroke="white"
-            strokeWidth="1.5"
-            strokeLinecap="round"
-            stroke-linejoin="round"
-          />
-          <path
-            fillRule="evenodd"
-            clipRule="evenodd"
-            d="M5.69591 9.54996C5.69591 7.40863 7.41283 5.69171 9.55508 5.69171C11.6964 5.69171 13.4133 7.40863 13.4133 9.54996C13.4133 11.6913 11.6964 13.4082 9.55508 13.4082C7.41283 13.4082 5.69591 11.6913 5.69591 9.54996Z"
-            stroke="white"
-            strokeWidth="1.5"
-            strokeLinecap="round"
-            stroke-linejoin="round"
-          />
-        </svg>
+          <svg
+            width="18"
+            height="18"
+            viewBox="0 0 19 19"
+            fill="none"
+            xmlns="http://www.w3.org/2000/svg"
+          >
+            <path
+              d="M15.207 12.4403C16.8094 12.4403 18.1092 11.1414 18.1092 9.53907C18.1092 7.93673 16.8094 6.63782 15.207 6.63782"
+              stroke="white"
+              strokeWidth="1.5"
+              strokeLinecap="round"
+              strokeLinejoin="round"
+            />
+            <path
+              d="M3.90217 12.4403C2.29983 12.4403 1 11.1414 1 9.53907C1 7.93673 2.29983 6.63782 3.90217 6.63782"
+              stroke="white"
+              strokeWidth="1.5"
+              strokeLinecap="round"
+              strokeLinejoin="round"
+            />
+            <path
+              fillRule="evenodd"
+              clipRule="evenodd"
+              d="M9.54993 13.4133C7.4086 13.4133 5.69168 11.6964 5.69168 9.55417C5.69168 7.41284 7.4086 5.69592 9.54993 5.69592C11.6913 5.69592 13.4082 7.41284 13.4082 9.55417C13.4082 11.6964 11.6913 13.4133 9.54993 13.4133Z"
+              stroke="white"
+              strokeWidth="1.5"
+              strokeLinecap="round"
+              strokeLinejoin="round"
+            />
+            <path
+              d="M6.66895 15.207C6.66895 16.8094 7.96787 18.1092 9.5702 18.1092C11.1725 18.1092 12.4715 16.8094 12.4715 15.207"
+              stroke="white"
+              strokeWidth="1.5"
+              strokeLinecap="round"
+              strokeLinejoin="round"
+            />
+            <path
+              d="M6.66895 3.90217C6.66895 2.29983 7.96787 1 9.5702 1C11.1725 1 12.4715 2.29983 12.4715 3.90217"
+              stroke="white"
+              strokeWidth="1.5"
+              strokeLinecap="round"
+              strokeLinejoin="round"
+            />
+            <path
+              fillRule="evenodd"
+              clipRule="evenodd"
+              d="M5.69591 9.54996C5.69591 7.40863 7.41283 5.69171 9.55508 5.69171C11.6964 5.69171 13.4133 7.40863 13.4133 9.54996C13.4133 11.6913 11.6964 13.4082 9.55508 13.4082C7.41283 13.4082 5.69591 11.6913 5.69591 9.54996Z"
+              stroke="white"
+              strokeWidth="1.5"
+              strokeLinecap="round"
+              strokeLinejoin="round"
+            />
+          </svg>
         </IconWrapper>
         <Spacer inline />
-        <Input 
-          placeholder="ex: my-cluster" 
+        <Input
+          placeholder="ex: my-cluster"
           width="100%"
           value={clusterName}
           setValue={setClusterName}
         />
       </Flex>
       <Spacer y={1} />
-      <Button 
+      <Button
         onClick={renameCluster}
         disabled={clusterName === ""}
         status={status}

+ 27 - 27
dashboard/src/main/home/cluster-dashboard/dashboard/Dashboard.tsx

@@ -71,14 +71,14 @@ export const Dashboard: React.FunctionComponent = () => {
     if (
       context.currentCluster.status !== "UPDATING_UNAVAILABLE" &&
       !tabOptions.find((tab) => tab.value === "nodes")
-    ) {  
+    ) {
       if (!context.currentProject.capi_provisioner_enabled) {
         tabOptions.unshift({ label: "Namespaces", value: "namespaces" });
       }
       tabOptions.unshift({ label: "Metrics", value: "metrics" });
-      tabOptions.unshift({ label: "Nodes", value: "nodes" }); 
+      tabOptions.unshift({ label: "Nodes", value: "nodes" });
     }
-    
+
     if (
       context.currentProject.capi_provisioner_enabled &&
       !tabOptions.find((tab) => tab.value === "configuration")
@@ -147,16 +147,16 @@ export const Dashboard: React.FunctionComponent = () => {
 
     return (
       <>
-      <Bolded>To configure custom domains for your apps, add a CNAME record pointing to the following Ingress IP:</Bolded>
-      <br /><br />
-      <CopyToClipboard
-        as={Url}
-        text={ingressIp}
-        wrapperProps={{ onClick: (e: any) => e.stopPropagation() }}
-      >
-        <span>{ingressIp}</span>
-        <i className="material-icons-outlined">content_copy</i>
-      </CopyToClipboard>
+        <Bolded>To configure custom domains for your apps, add a CNAME record pointing to the following Ingress IP:</Bolded>
+        <br /><br />
+        <CopyToClipboard
+          as={Url}
+          text={ingressIp}
+          wrapperProps={{ onClick: (e: any) => e.stopPropagation() }}
+        >
+          <span>{ingressIp}</span>
+          <i className="material-icons-outlined">content_copy</i>
+        </CopyToClipboard>
       </>
     );
   };
@@ -176,7 +176,7 @@ export const Dashboard: React.FunctionComponent = () => {
         setIngressIp(ingress_ip);
         setIngressError(ingress_error);
       }
-    } catch (error) {}
+    } catch (error) { }
   };
 
   useEffect(() => {
@@ -199,13 +199,13 @@ export const Dashboard: React.FunctionComponent = () => {
               context.currentCluster.status === "UPDATING_UNAVAILABLE"
             )
           ) && (
-            <>
-              <ProvisionerStatus
-                provisionFailureReason={provisionFailureReason}
-              />
-              <Spacer y={1} />
-            </>
-          )}
+              <>
+                <ProvisionerStatus
+                  provisionFailureReason={provisionFailureReason}
+                />
+                <Spacer y={1} />
+              </>
+            )}
           <TabSelector
             options={currentTabOptions}
             currentTab={currentTab}
@@ -246,14 +246,14 @@ export const Dashboard: React.FunctionComponent = () => {
                   stroke="white"
                   strokeWidth="1.5"
                   strokeLinecap="round"
-                  stroke-linejoin="round"
+                  strokeLinejoin="round"
                 />
                 <path
                   d="M3.90217 12.4403C2.29983 12.4403 1 11.1414 1 9.53907C1 7.93673 2.29983 6.63782 3.90217 6.63782"
                   stroke="white"
                   strokeWidth="1.5"
                   strokeLinecap="round"
-                  stroke-linejoin="round"
+                  strokeLinejoin="round"
                 />
                 <path
                   fillRule="evenodd"
@@ -262,21 +262,21 @@ export const Dashboard: React.FunctionComponent = () => {
                   stroke="white"
                   strokeWidth="1.5"
                   strokeLinecap="round"
-                  stroke-linejoin="round"
+                  strokeLinejoin="round"
                 />
                 <path
                   d="M6.66895 15.207C6.66895 16.8094 7.96787 18.1092 9.5702 18.1092C11.1725 18.1092 12.4715 16.8094 12.4715 15.207"
                   stroke="white"
                   strokeWidth="1.5"
                   strokeLinecap="round"
-                  stroke-linejoin="round"
+                  strokeLinejoin="round"
                 />
                 <path
                   d="M6.66895 3.90217C6.66895 2.29983 7.96787 1 9.5702 1C11.1725 1 12.4715 2.29983 12.4715 3.90217"
                   stroke="white"
                   strokeWidth="1.5"
                   strokeLinecap="round"
-                  stroke-linejoin="round"
+                  strokeLinejoin="round"
                 />
                 <path
                   fillRule="evenodd"
@@ -285,7 +285,7 @@ export const Dashboard: React.FunctionComponent = () => {
                   stroke="white"
                   strokeWidth="1.5"
                   strokeLinecap="round"
-                  stroke-linejoin="round"
+                  strokeLinejoin="round"
                 />
               </svg>
               <Spacer inline />

+ 8 - 8
dashboard/src/main/home/dashboard/ClusterList.tsx

@@ -13,10 +13,10 @@ import Helper from "components/form-components/Helper";
 
 type Props = {};
 
-const ClusterList: React.FC<Props> = ({}) => {
+const ClusterList: React.FC<Props> = ({ }) => {
   const { currentProject, setCurrentCluster } = useContext(Context);
   const [isLoading, setIsLoading] = useState(true);
-  const [clusters, setClusters] = useState(null);
+  const [clusters, setClusters] = useState([]);
   const location = useLocation();
   const history = useHistory();
 
@@ -51,14 +51,14 @@ const ClusterList: React.FC<Props> = ({}) => {
             stroke="white"
             strokeWidth="1.5"
             strokeLinecap="round"
-            stroke-linejoin="round"
+            strokeLinejoin="round"
           />
           <path
             d="M3.90217 12.4403C2.29983 12.4403 1 11.1414 1 9.53907C1 7.93673 2.29983 6.63782 3.90217 6.63782"
             stroke="white"
             strokeWidth="1.5"
             strokeLinecap="round"
-            stroke-linejoin="round"
+            strokeLinejoin="round"
           />
           <path
             fillRule="evenodd"
@@ -67,21 +67,21 @@ const ClusterList: React.FC<Props> = ({}) => {
             stroke="white"
             strokeWidth="1.5"
             strokeLinecap="round"
-            stroke-linejoin="round"
+            strokeLinejoin="round"
           />
           <path
             d="M6.66895 15.207C6.66895 16.8094 7.96787 18.1092 9.5702 18.1092C11.1725 18.1092 12.4715 16.8094 12.4715 15.207"
             stroke="white"
             strokeWidth="1.5"
             strokeLinecap="round"
-            stroke-linejoin="round"
+            strokeLinejoin="round"
           />
           <path
             d="M6.66895 3.90217C6.66895 2.29983 7.96787 1 9.5702 1C11.1725 1 12.4715 2.29983 12.4715 3.90217"
             stroke="white"
             strokeWidth="1.5"
             strokeLinecap="round"
-            stroke-linejoin="round"
+            strokeLinejoin="round"
           />
           <path
             fillRule="evenodd"
@@ -90,7 +90,7 @@ const ClusterList: React.FC<Props> = ({}) => {
             stroke="white"
             strokeWidth="1.5"
             strokeLinecap="round"
-            stroke-linejoin="round"
+            strokeLinejoin="round"
           />
         </svg>
       </DashboardIcon>

+ 6 - 6
dashboard/src/main/home/dashboard/OldClusterList.tsx

@@ -77,14 +77,14 @@ class Templates extends Component<PropsType, StateType> {
             stroke="white"
             strokeWidth="1.5"
             strokeLinecap="round"
-            stroke-linejoin="round"
+            strokeLinejoin="round"
           />
           <path
             d="M3.90217 12.4403C2.29983 12.4403 1 11.1414 1 9.53907C1 7.93673 2.29983 6.63782 3.90217 6.63782"
             stroke="white"
             strokeWidth="1.5"
             strokeLinecap="round"
-            stroke-linejoin="round"
+            strokeLinejoin="round"
           />
           <path
             fillRule="evenodd"
@@ -93,21 +93,21 @@ class Templates extends Component<PropsType, StateType> {
             stroke="white"
             strokeWidth="1.5"
             strokeLinecap="round"
-            stroke-linejoin="round"
+            strokeLinejoin="round"
           />
           <path
             d="M6.66895 15.207C6.66895 16.8094 7.96787 18.1092 9.5702 18.1092C11.1725 18.1092 12.4715 16.8094 12.4715 15.207"
             stroke="white"
             strokeWidth="1.5"
             strokeLinecap="round"
-            stroke-linejoin="round"
+            strokeLinejoin="round"
           />
           <path
             d="M6.66895 3.90217C6.66895 2.29983 7.96787 1 9.5702 1C11.1725 1 12.4715 2.29983 12.4715 3.90217"
             stroke="white"
             strokeWidth="1.5"
             strokeLinecap="round"
-            stroke-linejoin="round"
+            strokeLinejoin="round"
           />
           <path
             fillRule="evenodd"
@@ -116,7 +116,7 @@ class Templates extends Component<PropsType, StateType> {
             stroke="white"
             strokeWidth="1.5"
             strokeLinecap="round"
-            stroke-linejoin="round"
+            strokeLinejoin="round"
           />
         </svg>
       </DashboardIcon>

+ 1 - 1
dashboard/src/main/home/onboarding/Onboarding.tsx

@@ -20,7 +20,7 @@ const Onboarding = () => {
   useSteps(isLoading);
 
   useEffect(() => {
-    let unsub = devtools(OFState, "Onboarding flow state");
+    let unsub = devtools(OFState, { name: "Onboarding flow state" });
     return () => {
       if (typeof unsub === "function") {
         unsub();

+ 4 - 4
dashboard/src/main/home/sidebar/SidebarLink.tsx

@@ -4,8 +4,8 @@ import { Context } from "shared/Context";
 import { useRouting } from "shared/routing";
 
 const SidebarLink: React.FC<
-  { path: string; targetClusterName?: string } & Omit<NavLinkProps, "to">
-> = ({ children, path, ...props }) => {
+  { path: string; targetClusterName?: string, active?: boolean } & Omit<NavLinkProps, "to">
+> = ({ children, path, targetClusterName, active, ...rest }) => {
   const params = useParams<{ namespace: string }>();
   const { getQueryParam } = useRouting();
   const { currentCluster, currentProject } = useContext(Context);
@@ -18,7 +18,7 @@ const SidebarLink: React.FC<
     let pathNamespace = params.namespace;
     const search = new URLSearchParams();
     if (currentCluster?.name) {
-      search.append("cluster", props.targetClusterName || currentCluster.name);
+      search.append("cluster", targetClusterName || currentCluster.name);
     }
 
     if (currentProject?.id) {
@@ -41,7 +41,7 @@ const SidebarLink: React.FC<
   };
 
   return (
-    <NavLink to={withQueryParams(path)} {...props}>
+    <NavLink to={withQueryParams(path)} {...rest}>
       {children}
     </NavLink>
   );