sunguroku 3 лет назад
Родитель
Сommit
20f1712ea1

+ 61 - 0
api/server/handlers/project_integration/preflight_check_aws.go

@@ -0,0 +1,61 @@
+package project_integration
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/bufbuild/connect-go"
+	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"
+)
+
+type CreatePreflightCheckAWSHandler struct {
+	handlers.PorterHandlerReadWriter
+}
+
+func NewCreatePreflightCheckAWSHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *CreatePreflightCheckAWSHandler {
+	return &CreatePreflightCheckAWSHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+	}
+}
+
+func (p *CreatePreflightCheckAWSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	project, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+	ctx := r.Context()
+
+	request := &types.RolePreflightCheckRequest{}
+	if ok := p.DecodeAndValidate(w, r, request); !ok {
+		return
+	}
+
+	res := types.RolePreflightCheckResponse{
+		TargetARN: 	     request.TargetARN,
+	}
+
+	checkReq := porterv1.RolePreflightCheckRequest{
+		ProjectID:       int64(project.ID),
+		TargetARN:		 request.TargetARN,
+		ExternalID: 	 request.ExternalID,
+	}
+
+	checkResp, err := p.Config().ClusterControlPlaneClient.RolePreflightCheck(ctx, connect.NewRequest(&checkReq))
+
+	if err != nil {
+		e := fmt.Errorf("preflight check failed: %w", err)
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(e))
+		return
+	}
+	
+	res.TargetARN = checkResp.Msg.TargetArn
+
+	p.WriteResult(w, r, checkResp)
+}

+ 10 - 0
api/types/project_integration.go

@@ -75,6 +75,16 @@ type AWSIntegration struct {
 
 type ListAWSResponse []*AWSIntegration
 
+type RolePreflightCheckRequest struct {
+	ProjectID	  uint	 `json:"project_id"`
+	TargetARN     string `json:"target_arn"`
+	ExternalID	  string `json:"external_id"`
+}
+
+type RolePreflightCheckResponse struct {
+	TargetARN     string `json:"target_arn"`
+}
+
 type CreateAWSRequest struct {
 	AWSRegion          string `json:"aws_region"`
 	AWSClusterID       string `json:"aws_cluster_id"`

+ 220 - 0
dashboard/src/components/CloudFormationForm.tsx

@@ -0,0 +1,220 @@
+import React, { useEffect, useState, useContext, useMemo } from "react";
+import styled from "styled-components";
+
+import api from "shared/api";
+import aws from "assets/aws.png";
+
+import { Context } from "shared/Context";
+
+import Heading from "components/form-components/Heading";
+import Helper from "./form-components/Helper";
+import InputRow from "./form-components/InputRow";
+import SaveButton from "./SaveButton";
+import Loading from "./Loading";
+
+type Props = {
+  goBack: () => void;
+  proceed: () => void;
+};
+
+type AWSCredential = {
+  created_at: string;
+  id: number;
+  user_id: number;
+  project_id: number;
+  aws_arn: string;
+};
+
+
+const CloudFormationForm: React.FC<Props> = ({
+  goBack,
+  proceed,
+}) => {
+  const { currentProject } = useContext(Context);
+  const [AWSCredentials, setAWSCredentials] = useState<AWSCredential[]>(null);
+  const [isLoading, setIsLoading] = useState(true);
+  const [AWSAccountID, setAWSAccountID] = useState("");
+  const [selectedCredentials, setSelectedCredentials] = useState<AWSCredential>(null);
+  const [showCreateForm, setShowCreateForm] = useState(false);
+  const [roleStatus, setRoleStatus] = useState("");
+
+  const checkIfRoleExists = () => {
+    setRoleStatus("loading");
+    // api
+    //   .preflightCheckAWS(
+    //     "<token>",
+    //     {
+    //       target_arn: targetARN,
+    //       external_id: externalID,
+    //     },
+    //     {
+    //       id: currentProject.id,
+    //     }
+    //   )
+    //   .then(({ data }) => {
+    //     setRoleStatus("successful");
+    //     proceed();
+    //   })
+    //   .catch((err) => {
+    //     console.error(err);
+    //     setCreateStatus("Error creating credentials");
+    //   });
+      setRoleStatus("successful");
+      proceed();
+  };
+
+  const renderContent = () => {
+    return (
+      <>
+        <StyledForm>
+          {
+            AWSCredentials.length > 0 && (
+              <CloseButton onClick={() => setShowCreateForm(false)}>
+                <i className="material-icons">close</i>
+              </CloseButton>
+            )
+          }
+          <InputRow
+            type="string"
+            value={AWSAccountID}
+            setValue={(e: string) => setAWSAccountID(e)}
+            label="👤 AWS Account ID"
+            placeholder="ex: 915037676314"
+            isRequired
+          />
+          <SaveButton
+            disabled={AWSAccountID === ""}
+            onClick={() => { window.open(
+                  `https://console.aws.amazon.com/cloudformation/home?#/stacks/create/review?templateURL=https://s3.eu-central-1.amazonaws.com/cloudformation-templates-eu-central-1&param_KeyName=test`
+                )
+              }
+            }
+            clearPosition
+            text="Grant Permissions"
+          />
+        </StyledForm>
+        <SaveButton
+          onClick={checkIfRoleExists}
+          status={roleStatus}
+          statusPosition="right"
+          clearPosition
+          text="Continue"
+        />
+      </>
+    );
+  }
+
+  return (
+    <>
+      <Heading isAtTop>
+        <BackButton width="140px" onClick={goBack}>
+          <i className="material-icons">first_page</i>
+          Select cloud
+        </BackButton>
+        <Spacer />
+        <Img src={aws} />
+        Grant AWS Permissions
+      </Heading>
+      <Helper>
+        Grant Porter permissions to create infrastructure in your AWS account.
+      </Helper>
+      {
+        isLoading ? (
+          <Loading height="150px" />
+        ) : (
+          renderContent()
+        )
+      }
+    </>
+  );
+};
+
+export default CloudFormationForm;
+
+const CloseButton = styled.div`
+  position: absolute;
+  top: 15px;
+  right: 15px;
+  padding: 5px;
+  border-radius: 100px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  background: #ffffff11;
+  :hover {
+    background: #ffffff22;
+    > i {
+      color: #ffffff;
+    }
+  }
+  > i {
+    font-size: 20px;
+    color: #aaaabb;
+  }
+`;
+
+const Spacer = styled.div`
+  height: 1px;
+  width: 17px;
+`;
+
+const Icon = styled.img`
+  width: 15px;
+  margin-right: 15px;
+`;
+
+const CreateRow = styled.div`
+  height: 50px;
+  display: flex;
+  cursor: pointer;
+  align-items: center;
+  font-size: 13px;
+  padding: 20px;
+  background: #ffffff11;
+  :hover {
+    background: #ffffff18; 
+  }
+`;
+
+const Img = styled.img`
+  height: 18px;
+  margin-right: 15px;
+`;
+
+const BackButton = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  cursor: pointer;
+  font-size: 13px;
+  height: 35px;
+  padding: 5px 13px;
+  padding-right: 15px;
+  border: 1px solid #ffffff55;
+  border-radius: 100px;
+  width: ${(props: { width: string }) => props.width};
+  color: white;
+  background: #ffffff11;
+
+  :hover {
+    background: #ffffff22;
+  }
+
+  > i {
+    color: white;
+    font-size: 16px;
+    margin-right: 6px;
+    margin-left: -2px;
+  }
+`;
+
+const StyledForm = styled.div`
+  position: relative;
+  padding: 15px 30px 25px;
+  border-radius: 5px;
+  background: #26292e;
+  border: 1px solid #494b4f;
+  font-size: 13px;
+  margin-bottom: 30px;
+`;

+ 11 - 24
dashboard/src/shared/api.tsx

@@ -71,6 +71,16 @@ const getGitlabIntegration = baseApi<{}, { project_id: number }>(
   ({ project_id }) => `/api/projects/${project_id}/integrations/gitlab`
 );
 
+const preflightCheckAWS = baseApi<
+  {
+    target_arn: string;
+    external_id: string;
+  },
+  { id: number }
+>("POST", (pathParams) => {
+  return `/api/projects/${pathParams.id}/integrations/aws/preflight`;
+});
+
 const createAWSIntegration = baseApi<
   {
     aws_region: string;
@@ -469,29 +479,6 @@ const getPRDeploymentByID = baseApi<
   return `/api/projects/${project_id}/clusters/${cluster_id}/environments/${environment_id}/deployment`;
 });
 
-// TODO (soham): Check if we are really using this?
-// const getPRDeployment = baseApi<
-//   {
-//     namespace: string;
-//   },
-//   {
-//     cluster_id: number;
-//     project_id: number;
-//     git_installation_id: number;
-//     git_repo_owner: string;
-//     git_repo_name: string;
-//   }
-// >("GET", (pathParams) => {
-//   const {
-//     cluster_id,
-//     project_id,
-//     git_installation_id,
-//     git_repo_owner,
-//     git_repo_name,
-//   } = pathParams;
-//   return `/api/projects/${project_id}/gitrepos/${git_installation_id}/${git_repo_owner}/${git_repo_name}/clusters/${cluster_id}/deployment`;
-// });
-
 const deletePRDeployment = baseApi<
   {},
   {
@@ -2460,7 +2447,6 @@ export default {
   getConfigMap,
   getPRDeploymentList,
   getPRDeploymentByID,
-  // getPRDeployment,
   getGHAWorkflowTemplate,
   getGitRepoList,
   getGitRepoPermission,
@@ -2566,6 +2552,7 @@ export default {
   addApplicationToEnvGroup,
   removeApplicationFromEnvGroup,
   provisionDatabase,
+  preflightCheckAWS,
   getDatabases,
   getPreviousLogsForContainer,
   upgradePorterAgent,