Ver código fonte

Submit auto checks (#3717)

sdess09 2 anos atrás
pai
commit
fde1ad2506

+ 15 - 0
api/server/handlers/project/update_onboarding_step.go

@@ -191,6 +191,21 @@ func (v *UpdateOnboardingStepHandler) ServeHTTP(w http.ResponseWriter, r *http.R
 		}
 	}
 
+	if request.Step == "requested-quota-increase" {
+		err := v.Config().AnalyticsClient.Track(analytics.QuotaIncreaseAttemptTrack(&analytics.ProvisioningAttemptTrackOpts{
+			ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(user.ID, project.ID),
+			Email:                  user.Email,
+			FirstName:              user.FirstName,
+			LastName:               user.LastName,
+			CompanyName:            user.CompanyName,
+			Region:                 request.Region,
+			Provider:               request.Provider,
+		}))
+		if err != nil {
+			_ = telemetry.Error(ctx, span, err, "error tracking quota increase")
+		}
+	}
+
 	if request.Step == "provisioning-started" {
 		err := v.Config().AnalyticsClient.Track(analytics.ProvisioningAttemptTrack(&analytics.ProvisioningAttemptTrackOpts{
 			ProjectScopedTrackOpts: analytics.GetProjectScopedTrackOpts(user.ID, project.ID),

+ 70 - 0
api/server/handlers/project_integration/request_quota_increase.go

@@ -0,0 +1,70 @@
+package project_integration
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/porter-dev/api-contracts/generated/go/helpers"
+
+	"connectrpc.com/connect"
+	porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/internal/telemetry"
+)
+
+// CreateRequestQuotaIncreaseHandler requests quota increase for given a list of quotas
+type CreateRequestQuotaIncreaseHandler struct {
+	handlers.PorterHandlerReadWriter
+}
+
+// NewRequestQuotaIncreaseHandler requests quota increase for given a list of quotas
+func NewRequestQuotaIncreaseHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *CreateRequestQuotaIncreaseHandler {
+	return &CreateRequestQuotaIncreaseHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+	}
+}
+
+func (p *CreateRequestQuotaIncreaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	ctx, span := telemetry.NewSpan(r.Context(), "quota-increase")
+	defer span.End()
+	project, _ := ctx.Value(types.ProjectScope).(*models.Project)
+
+	quotaIncreaseValues := &porterv1.QuotaIncreaseRequest{}
+	err := helpers.UnmarshalContractObjectFromReader(r.Body, quotaIncreaseValues)
+	if err != nil {
+		e := telemetry.Error(ctx, span, err, "error unmarshalling quota check increases")
+		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(e, http.StatusPreconditionFailed, err.Error()))
+		return
+	}
+
+	input := porterv1.QuotaIncreaseRequest{
+		ProjectId:                  int64(project.ID),
+		CloudProvider:              quotaIncreaseValues.CloudProvider,
+		CloudProviderCredentialsId: quotaIncreaseValues.CloudProviderCredentialsId,
+		QuotaIncreases:             quotaIncreaseValues.QuotaIncreases,
+	}
+
+	if quotaIncreaseValues.PreflightValues != nil {
+		if quotaIncreaseValues.CloudProvider == porterv1.EnumCloudProvider_ENUM_CLOUD_PROVIDER_GCP || quotaIncreaseValues.CloudProvider == porterv1.EnumCloudProvider_ENUM_CLOUD_PROVIDER_AWS {
+			input.PreflightValues = quotaIncreaseValues.PreflightValues
+		}
+	}
+
+	checkResp, err := p.Config().ClusterControlPlaneClient.QuotaIncrease(ctx, connect.NewRequest(&input))
+	if err != nil {
+		e := fmt.Errorf("quota increase request failed: %w", err)
+		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(e, http.StatusPreconditionFailed, err.Error()))
+		return
+	}
+
+	p.WriteResult(w, r, checkResp)
+}

+ 28 - 0
api/server/router/project_integration.go

@@ -248,6 +248,34 @@ func getProjectIntegrationRoutes(
 		Router:   r,
 	})
 
+	// POST /api/projects/{project_id}/integrations/quotaincrease -> project_integration.NewCreatePreflightCheckHandler
+	requestQuotaIncreaseEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbCreate,
+			Method: types.HTTPVerbPost,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/quotaincrease",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+			},
+		},
+	)
+
+	requestQuotaIncreaseHandler := project_integration.NewRequestQuotaIncreaseHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: requestQuotaIncreaseEndpoint,
+		Handler:  requestQuotaIncreaseHandler,
+		Router:   r,
+	})
+
 	// GET /api/projects/{project_id}/integrations/azure -> project_integration.NewListAzureHandler
 	listAzureEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

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

@@ -8,18 +8,20 @@ import Text from "./porter/Text";
 import Error from "./porter/Error";
 import healthy from "assets/status-healthy.png";
 import failure from "assets/failure.svg";
-import { PREFLIGHT_MESSAGE_CONST, PREFLIGHT_MESSAGE_CONST_AWS, PREFLIGHT_MESSAGE_CONST_GCP } from "shared/util";
+import { PREFLIGHT_MESSAGE_CONST, PREFLIGHT_MESSAGE_CONST_AWS, PREFLIGHT_MESSAGE_CONST_GCP, PROVISIONING_STATUS } from "shared/util";
 import Loading from "./Loading";
 type Props = RouteComponentProps & {
   preflightData: any
-  provider: 'AWS' | 'GCP' | 'DEFAULT';
+  provider: 'AWS' | 'GCP' | 'DEFAULT | PROVISIONING_STATUS';
   error?: string;
 
 };
 
 const PreflightChecks: React.FC<Props> = (props) => {
-  const getMessageConstByProvider = (provider: 'AWS' | 'GCP' | 'DEFAULT') => {
+  const getMessageConstByProvider = (provider: 'AWS' | 'GCP' | 'DEFAULT' | 'PROVISIONING_STATUS') => {
     switch (provider) {
+      case 'PROVISIONING_STATUS':
+        return PROVISIONING_STATUS;
       case 'AWS':
         return PREFLIGHT_MESSAGE_CONST_AWS;
       case 'GCP':
@@ -35,7 +37,7 @@ const PreflightChecks: React.FC<Props> = (props) => {
     const checkData = props.preflightData?.preflight_checks?.[checkKey];
     const hasMessage = checkData?.message;
 
-    const [isExpanded, setIsExpanded] = useState(false);
+    const [isExpanded, setIsExpanded] = useState(true);
 
     const handleToggle = () => {
       if (hasMessage) {

+ 180 - 344
dashboard/src/components/ProvisionerSettings.tsx

@@ -23,12 +23,16 @@ import {
   EKSLogging,
   EKSPreflightValues,
   PreflightCheckRequest,
+  QuotaIncreaseRequest,
+  EnumQuotaIncrease,
   AWSClusterNetwork,
 } from "@porter-dev/api-contracts";
 
 import { ClusterType } from "shared/types";
 import Button from "./porter/Button";
 import Error from "./porter/Error";
+import healthy from "assets/status-healthy.png";
+
 import Spacer from "./porter/Spacer";
 import Step from "./porter/Step";
 import Link from "./porter/Link";
@@ -42,6 +46,8 @@ import Loading from "./Loading";
 import PreflightChecks from "./PreflightChecks";
 import Placeholder from "./Placeholder";
 import VerticalSteps from "./porter/VerticalSteps";
+import Modal from "components/porter/Modal";
+import { PREFLIGHT_TO_ENUM } from "shared/util";
 import { useIntercom } from "lib/hooks/useIntercom";
 const regionOptions = [
   { value: "us-east-1", label: "US East (N. Virginia) us-east-1" },
@@ -151,9 +157,13 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
   const [preflightData, setPreflightData] = useState(null)
   const [preflightFailed, setPreflightFailed] = useState<boolean>(true)
   const [preflightError, setPreflightError] = useState<string>("")
-  
+  const [showPreflightModal, setShowPreflightModal] = useState(false);
+  const [showHelpMessage, setShowHelpMessage] = useState(true);
+  const [quotaIncrease, setQuotaIncrease] = useState<EnumQuotaIncrease[]>([]);
+  const [showEmailMessage, setShowEmailMessage] = useState(false);
   const { showIntercomWithMessage } = useIntercom();
 
+
   const markStepStarted = async (step: string, errMessage?: string) => {
     try {
       await api.updateOnboardingStep(
@@ -179,18 +189,7 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
     }
     if (isReadOnly && props.provisionerError == "") {
       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;
   };
@@ -507,6 +506,49 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
     }
   }, [props.selectedClusterVersion, awsRegion]);
 
+  const proceedToProvision = async () => {
+    setShowEmailMessage(true)
+    markStepStarted("requested-quota-increase")
+    setStep(2)
+  }
+  const requestQuotasAndProvision = async () => {
+    await requestQuotaIncrease()
+    await createCluster()
+  }
+
+  const requestQuotaIncrease = async () => {
+
+    try {
+      setIsLoading(true);
+      var data = new QuotaIncreaseRequest({
+        projectId: BigInt(currentProject.id),
+        cloudProvider: EnumCloudProvider.AWS,
+        cloudProviderCredentialsId: props.credentialId,
+        preflightValues: {
+          case: "eksPreflightValues",
+          value: new EKSPreflightValues({
+            region: awsRegion,
+          })
+        },
+        quotaIncreases: quotaIncrease
+      });
+      await api.requestQuotaIncrease(
+        "<token>", data,
+        {
+          id: currentProject.id,
+        }
+      )
+
+
+      setIsLoading(false)
+    } catch (err) {
+      console.log(err)
+      setIsLoading(false)
+    }
+
+  }
+
+
 
   const preflightChecks = async () => {
 
@@ -515,6 +557,7 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
       setPreflightData(null);
       setPreflightFailed(true)
       setPreflightError("");
+      setShowEmailMessage(false)
 
       var data = new PreflightCheckRequest({
         projectId: BigInt(currentProject.id),
@@ -536,14 +579,19 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
       // Check if any of the preflight checks has a message
       let hasMessage = false;
       let errors = "Preflight Checks Failed : ";
+      let quotas: EnumQuotaIncrease[] = [];
       for (let check in preflightDataResp?.data?.Msg.preflight_checks) {
+
         if (preflightDataResp?.data?.Msg.preflight_checks[check]?.message) {
+          quotas.push(PREFLIGHT_TO_ENUM[check])
           hasMessage = true;
           errors = errors + check + ", "
         }
       }
+      setQuotaIncrease(quotas)
       // If none of the checks have a message, set setPreflightFailed to false
       if (hasMessage) {
+        setShowPreflightModal(true)
         showIntercomWithMessage({ message: "I am running into an issue provisioning a cluster." });
         markStepStarted("provisioning-failed", errors);
       }
@@ -1013,6 +1061,11 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
     );
   };
 
+  const dismissPreflight = () => {
+    setShowHelpMessage(false);
+    preflightChecks();
+  }
+
   const renderForm = () => {
     // Render simplified form if initial create
     if (!props.clusterId) {
@@ -1040,32 +1093,78 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
               </>
             </>,
             <>
-              <PreflightChecks provider='AWS' preflightData={preflightData} error={preflightError} />
-              <Spacer y={.5} />
-              {(preflightFailed && preflightData) &&
+              {showEmailMessage ?
+                <>
+                  <CheckItemContainer >
+                    <CheckItemTop >
+                      <StatusIcon src={healthy} />
+                      <Spacer inline x={1} />
+                      <Text style={{ marginLeft: '10px', flex: 1 }}>{"Porter will request to increase quotas when you provision"}</Text>
+                    </CheckItemTop>
+                  </CheckItemContainer>
+
+                </> :
                 <>
-                  <Text color="helper">
-                    Preflight checks for the account didn't pass. Please fix the issues and retry.
-                  </Text>
+                  <PreflightChecks provider='AWS' preflightData={preflightData} error={preflightError} />
                   <Spacer y={.5} />
-                  < Button
-                    // disabled={isDisabled()}
-                    disabled={isLoading}
-                    onClick={preflightChecks}
-                  >
-                    Retry Checks
-                  </Button>
+                  {(preflightFailed && preflightData) &&
+                    <>
+                      {(showHelpMessage) ? <>
+                        <Text color="helper">
+                          Your account currently is blocked from provisioning in {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}
+
+                          >
+                            Auto request increase
+                          </Button>
+                          <Button
+                            disabled={isLoading}
+                            onClick={dismissPreflight}
+                            color="#313539"
+                          >
+                            I'll do it myself
+                          </Button>
+                        </div>
+
+                      </> : (
+                        <><Text color="helper">
+                          Your account currently is blocked from provisioning in {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></>)}
+                    </>
+                  }
                 </>
               }
             </>,
             <>
               <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} />
+              </>
+              }
               <Button
                 // disabled={isDisabled()}
                 // disabled={isDisabled() || preflightFailed || isLoading}
-                disabled={preflightFailed || isLoading}
-                onClick={createCluster}
+                disabled={(preflightFailed && !showEmailMessage) || isLoading}
+                onClick={showEmailMessage ? requestQuotasAndProvision : createCluster}
                 status={getStatus()}
               >
                 Provision
@@ -1127,334 +1226,71 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
 export default withRouter(ProvisionerSettings);
 
 const ExpandHeader = styled.div<{ isExpanded: boolean }>`
-  display: flex;
-  align-items: center;
-  cursor: pointer;
+              display: flex;
+              align-items: center;
+              cursor: pointer;
   > i {
-    margin-right: 7px;
-    margin-left: -7px;
-    transform: ${(props) =>
+                margin - right: 7px;
+              margin-left: -7px;
+              transform: ${(props) =>
     props.isExpanded ? "rotate(0deg)" : "rotate(-90deg)"};
-    transition: transform 0.1s ease;
+              transition: transform 0.1s ease;
   }
-`;
+              `;
 
 const StyledForm = styled.div`
-  position: relative;
-  padding: 30px 30px 25px;
-  border-radius: 5px;
-  background: ${({ theme }) => theme.fg};
-  border: 1px solid #494b4f;
-  font-size: 13px;
-  margin-bottom: 30px;
-`;
+              position: relative;
+              padding: 30px 30px 25px;
+              border-radius: 5px;
+              background: ${({ theme }) => theme.fg};
+              border: 1px solid #494b4f;
+              font-size: 13px;
+              margin-bottom: 30px;
+              `;
 
 const FlexCenter = styled.div`
-  display: flex;
-  align-items: center;
-  gap: 3px;
-`;
+              display: flex;
+              align-items: center;
+              gap: 3px;
+              `;
 const Wrapper = styled.div`
-  transform: translateY(+13px);
-`;
+              transform: translateY(+13px);
+              `;
 
 const ErrorInLine = styled.div`
-  display: flex;
-  align-items: center;
-  font-size: 13px;
-  color: #ff3b62;
-  margin-top: 10px;
+              display: flex;
+              align-items: center;
+              font-size: 13px;
+              color: #ff3b62;
+              margin-top: 10px;
 
   > i {
-    font-size: 18px;
-    margin-right: 5px;
+                font - size: 18px;
+              margin-right: 5px;
   }
+              `;
+
+const CheckItemContainer = styled.div`
+  display: flex;
+  flex-direction: column;
+  border: 1px solid ${props => props.theme.border};
+  border-radius: 5px;
+  font-size: 13px;
+  width: 100%;
+  margin-bottom: 10px;
+  padding-left: 10px;
+  cursor: ${props => (props.hasMessage ? 'pointer' : 'default')};
+  background: ${props => props.theme.clickable.bg};
+
 `;
 
-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;
-  }
-};
+const CheckItemTop = styled.div`
+  display: flex;
+  align-items: center;
+  padding: 10px;
+  background: ${props => props.theme.clickable.bg};
+`;
+
+const StatusIcon = styled.img`
+height: 14px;
+`;

+ 9 - 1
dashboard/src/shared/api.tsx

@@ -11,7 +11,7 @@ import {
   CreateStackBody,
   SourceConfig,
 } from "main/home/cluster-dashboard/stacks/types";
-import { Contract, PreflightCheckRequest } from "@porter-dev/api-contracts";
+import { Contract, PreflightCheckRequest, QuotaIncreaseRequest } from "@porter-dev/api-contracts";
 
 /**
  * Generic api call format
@@ -82,6 +82,13 @@ const preflightCheck = baseApi<PreflightCheckRequest, { id: number }>(
   }
 );
 
+const requestQuotaIncrease = baseApi<QuotaIncreaseRequest, { id: number }>(
+  "POST",
+  (pathParams) => {
+    return `/api/projects/${pathParams.id}/integrations/quotaincrease`;
+  }
+);
+
 
 const createAWSIntegration = baseApi<
   {
@@ -3305,6 +3312,7 @@ export default {
   removeApplicationFromEnvGroup,
   provisionDatabase,
   preflightCheck,
+  requestQuotaIncrease,
   getDatabases,
   getPreviousLogsForContainer,
   upgradePorterAgent,

+ 16 - 1
dashboard/src/shared/util.ts

@@ -1,3 +1,5 @@
+import { EnumQuotaIncrease } from "@porter-dev/api-contracts";
+
 export const isJSON = (value: string): boolean => {
   try {
     JSON.parse(value);
@@ -26,5 +28,18 @@ export const PREFLIGHT_MESSAGE_CONST_AWS = {
 export const PREFLIGHT_MESSAGE_CONST_GCP = {
   "apiEnabled": "APIs enabled on service account",
   "cidrAvailability": "CIDR availability",
-  "iamPermissions:": "IAM permissions",
+  "iamPermissions": "IAM permissions",
+}
+
+export const PREFLIGHT_TO_ENUM = {
+  "eip": EnumQuotaIncrease.AWS_EIP,
+  "natGateway": EnumQuotaIncrease.AWS_NAT,
+  "vpc": EnumQuotaIncrease.AWS_VPC,
+  "vcpus": EnumQuotaIncrease.AWS_VCPU,
+}
+
+export const PROVISIONING_STATUS = {
+  0: "Waiting for Quota Increase",
+  1: "Generating Resources",
+  2: "Provisioning Cluster ",
 }

+ 1 - 0
internal/analytics/track_events.go

@@ -19,6 +19,7 @@ const (
 	AWSLoginRedirect            SegmentEvent = "AWS Login Redirect"
 	AWSCreateIntegrationSuccess SegmentEvent = "AWS Create Integration Success"
 	AWSCreateIntegrationFailure SegmentEvent = "AWS Create Integration Failure"
+	QuotaIncreaseRequested      SegmentEvent = "AWS Quota Increase Requested"
 	ProvisioningAttempted       SegmentEvent = "Provisioning Attempted"
 	ProvisioningFailure         SegmentEvent = "Provisioning Failure"
 

+ 15 - 0
internal/analytics/tracks.go

@@ -404,6 +404,21 @@ func ProvisioningAttemptTrack(opts *ProvisioningAttemptTrackOpts) segmentTrack {
 	)
 }
 
+// QuotaIncreaseAttemptTrack returns a track for when a user attempts provisioning
+func QuotaIncreaseAttemptTrack(opts *ProvisioningAttemptTrackOpts) segmentTrack {
+	additionalProps := make(map[string]interface{})
+	additionalProps["email"] = opts.Email
+	additionalProps["name"] = opts.FirstName + " " + opts.LastName
+	additionalProps["company"] = opts.CompanyName
+	additionalProps["region"] = opts.Region
+	additionalProps["provider"] = opts.Provider
+
+	return getSegmentProjectTrack(
+		opts.ProjectScopedTrackOpts,
+		getDefaultSegmentTrack(additionalProps, QuotaIncreaseRequested),
+	)
+}
+
 // PreProvisionCheckTrack returns a track for when a user attempts provisioning
 func ProvisionFailureTrack(opts *ProvisioningAttemptTrackOpts) segmentTrack {
 	additionalProps := make(map[string]interface{})