Ver Fonte

Merge branch 'master' of https://github.com/porter-dev/porter

Justin Rhee há 3 anos atrás
pai
commit
d0ab7c400b

+ 3 - 1
cli/cmd/preview/v2beta1/addon_resource.go

@@ -11,7 +11,9 @@ func (a *AddonResource) GetName() string {
 }
 
 func (a *AddonResource) GetDependsOn() []string {
-	var dependsOn []string
+	dependsOn := []string{
+		"get-env",
+	}
 
 	if a == nil || a.DependsOn == nil {
 		return dependsOn

+ 1 - 1
cli/cmd/preview/v2beta1/app_resource.go

@@ -74,7 +74,7 @@ func (a *AppResource) getV1Resource(b *Build) (*types.Resource, error) {
 
 	return &types.Resource{
 		Name:      a.GetName(),
-		DependsOn: append([]string{b.GetName()}, a.GetDependsOn()...),
+		DependsOn: append([]string{"get-env", b.GetName()}, a.GetDependsOn()...),
 		Source: map[string]any{
 			"name":    a.Chart.GetName(),
 			"repo":    a.Chart.GetURL(),

+ 11 - 0
cli/cmd/preview/v2beta1/apply.go

@@ -3,6 +3,7 @@ package v2beta1
 import (
 	"context"
 	"fmt"
+	"regexp"
 
 	api "github.com/porter-dev/porter/api/client"
 	"github.com/porter-dev/porter/cli/cmd/config"
@@ -28,6 +29,10 @@ type PreviewApplier struct {
 }
 
 func NewApplier(client *api.Client, raw []byte, namespace string) (*PreviewApplier, error) {
+	// replace all instances of ${{ porter.env.FOO }} with { .get-env.FOO }
+	re := regexp.MustCompile(`\$\{\{\s*porter\.env\.(.*)\s*\}\}`)
+	raw = re.ReplaceAll(raw, []byte("{.get-env.$1}"))
+
 	parsed := &PorterYAML{}
 
 	err := yaml.Unmarshal(raw, parsed)
@@ -147,6 +152,12 @@ func (a *PreviewApplier) DowngradeToV1() (*types.ResourceGroup, error) {
 
 	v1File := &types.ResourceGroup{
 		Version: "v1",
+		Resources: []*types.Resource{
+			{
+				Name:   "get-env",
+				Driver: "os-env",
+			},
+		},
 	}
 
 	buildRefs := make(map[string]*Build)

+ 4 - 0
cli/cmd/preview/v2beta1/build.go

@@ -164,6 +164,9 @@ func (b *Build) getV1BuildImage() (*types.Resource, error) {
 		Target: map[string]any{
 			"app_name": b.GetName(),
 		},
+		DependsOn: []string{
+			"get-env",
+		},
 		Config: rawConfig,
 	}, nil
 }
@@ -184,6 +187,7 @@ func (b *Build) getV1PushImage() (*types.Resource, error) {
 		Name:   b.GetName(),
 		Driver: "push-image",
 		DependsOn: []string{
+			"get-env",
 			fmt.Sprintf("%s-build-image", b.GetName()),
 		},
 		Target: map[string]any{

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

@@ -21,20 +21,17 @@ import Link from "./porter/Link";
 
 type Props = {
   goBack: () => void;
-  AWSAccountID: string;
-  setAWSAccountID: (id: string) => void;
   proceed: (id: string) => void;
 };
 
 const CloudFormationForm: React.FC<Props> = ({
   goBack,
   proceed,
-  AWSAccountID,
-  setAWSAccountID
 }) => {
   const [grantPermissionsError, setGrantPermissionsError] = useState("");
   const [roleStatus, setRoleStatus] = useState("");
   const [errorMessage, setErrorMessage] = useState(undefined);
+  const [AWSAccountID, setAWSAccountID] = useState("");
   const { currentProject } = useContext(Context);
 
   const getExternalId = () => {

+ 1 - 0
dashboard/src/components/CopyToClipboard.tsx

@@ -10,6 +10,7 @@ type PropsType = {
   onError?: (e: ClipboardJS.Event) => void;
   wrapperProps?: any;
   as?: any;
+  children: JSX.Element[];
 };
 
 type StateType = {

+ 1 - 3
dashboard/src/components/CredentialsForm.tsx

@@ -21,7 +21,7 @@ import Spacer from "./porter/Spacer";
 
 type Props = {
   goBack: () => void;
-  proceed: (x: any) => void;
+  proceed: (cloud_provider_credentials_id: string) => void;
   enableAssumeRole?: () => void;
 };
 
@@ -80,10 +80,8 @@ const CredentialsForm: React.FC<Props> = ({
           // Hardcoded for backward-compatibility
           // TODO: remove
           aws_region: "us-east-f",
-
           aws_access_key_id: awsAccessKeyID,
           aws_secret_access_key: awsSecretAccessKey,
-          aws_assume_role_arn: "",
         },
         {
           id: currentProject.id,

+ 0 - 4
dashboard/src/components/ProvisionerFlow.tsx

@@ -30,7 +30,6 @@ const ProvisionerFlow: React.FC<Props> = ({
   const [credentialId, setCredentialId] = useState("");
   const [showCostConfirmModal, setShowCostConfirmModal] = useState(false);
   const [confirmCost, setConfirmCost] = useState("");
-  const [AWSAccountID, setAWSAccountID] = useState("");
   const [useAssumeRole, setUseAssumeRole] = useState(false);
 
   const isUsageExceeded = useMemo(() => {
@@ -162,8 +161,6 @@ const ProvisionerFlow: React.FC<Props> = ({
           setCredentialId(id);
           setCurrentStep("cluster");
         }}
-        AWSAccountID={AWSAccountID}
-        setAWSAccountID={setAWSAccountID}
       />
     );
   } else if (currentStep === "credentials" && !useAssumeRole) {
@@ -182,7 +179,6 @@ const ProvisionerFlow: React.FC<Props> = ({
       <ProvisionerForm
         goBack={() => setCurrentStep("credentials")}
         credentialId={credentialId}
-        AWSAccountID={AWSAccountID}
         useAssumeRole={useAssumeRole}
       />
     );

+ 1 - 3
dashboard/src/components/ProvisionerForm.tsx

@@ -13,14 +13,12 @@ import Spacer from "./porter/Spacer";
 type Props = {
   goBack: () => void;
   credentialId: string;
-  AWSAccountID: string;
   useAssumeRole?: boolean;
 };
 
 const ProvisionerForm: React.FC<Props> = ({
   goBack,
   credentialId,
-  AWSAccountID,
   useAssumeRole,
 }) => {
   return (
@@ -40,7 +38,7 @@ const ProvisionerForm: React.FC<Props> = ({
       </Text>
       <Spacer y={1} />
       {useAssumeRole ? (
-        <ProvisionerSettings credentialId={credentialId} AWSAccountID={AWSAccountID} />
+        <ProvisionerSettings credentialId={credentialId} />
       ) : (
         <ProvisionerSettingsOld credentialId={credentialId} />
       )}

+ 1 - 2
dashboard/src/components/ProvisionerSettings.tsx

@@ -56,7 +56,6 @@ const clusterVersionOptions = [
 type Props = RouteComponentProps & {
   selectedClusterVersion?: Contract;
   credentialId: string;
-  AWSAccountID: string;
   clusterId?: number;
 };
 
@@ -148,7 +147,7 @@ const ProvisionerSettings: React.FC<Props> = props => {
         .preflightCheckAWSUsage(
           "<token>",
           {
-            target_arn: `arn:aws:iam::${props.AWSAccountID}:role/porter-role`,
+            target_arn: props.credentialId,
             region: awsRegion
           },
           {

+ 7 - 5
dashboard/src/components/porter/LoadingBar.tsx

@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from "react";
+import React from "react";
 import styled, { keyframes } from "styled-components";
 
 type Props = {
@@ -22,7 +22,7 @@ const LoadingBar: React.FC<Props> = ({
         return color;
     }
   };
-  
+
   return (
     <StyledLoadingBar>
       <LoadingFill
@@ -52,15 +52,17 @@ const movingGradient = keyframes`
   }
 `;
 
-const LoadingFill = styled.div<{ 
+const LoadingFill = styled.div<{
   percent: string;
   color?: string;
 }>`
   width: ${props => props.percent};
-  background: ${props => props.color || "linear-gradient(to right, #8ce1ff, #616FEE)"};
+  background: ${props =>
+    props.color || "linear-gradient(to right, #8ce1ff, #616FEE)"};
   height: 100%;
   background-size: 250% 100%;
   animation: ${movingGradient} 2s infinite;
   animation-timing-function: ease-in-out;
   animation-direction: alternate;
-`;
+  transition: width 0.5s ease-in-out;
+`;

+ 48 - 54
dashboard/src/main/home/Home.tsx

@@ -1,6 +1,7 @@
 import React, { useEffect, useState, useContext, useRef } from "react";
 import { Route, RouteComponentProps, Switch, withRouter } from "react-router";
 import styled from "styled-components";
+import { createPortal } from "react-dom";
 
 import api from "shared/api";
 import { Context } from "shared/Context";
@@ -45,14 +46,15 @@ const GuardedIntegrations = fakeGuardedRoute("integrations", "", [
   "delete",
 ])(Integrations);
 
-type Props = RouteComponentProps & WithAuthProps & {
-  logOut: () => void;
-  currentProject: ProjectType;
-  currentCluster: ClusterType;
-  currentRoute: PorterUrl;
-};
+type Props = RouteComponentProps &
+  WithAuthProps & {
+    logOut: () => void;
+    currentProject: ProjectType;
+    currentCluster: ClusterType;
+    currentRoute: PorterUrl;
+  };
 
-const Home: React.FC<Props> = props => {
+const Home: React.FC<Props> = (props) => {
   const {
     user,
     projects,
@@ -93,7 +95,7 @@ const Home: React.FC<Props> = props => {
   const getMetadata = () => {
     api
       .getMetadata("<token>", {}, {})
-      .then(res => {
+      .then((res) => {
         setCapabilities(res.data);
       })
       .catch((err) => {
@@ -112,7 +114,7 @@ const Home: React.FC<Props> = props => {
 
     api
       .getProjects("<token>", {}, { id: user.userId })
-      .then(res => {
+      .then((res) => {
         if (res.data) {
           if (res.data.length === 0) {
             redirectToNewProject();
@@ -180,7 +182,7 @@ const Home: React.FC<Props> = props => {
         setHasFinishedOnboarding(true);
       }
     } catch (error) {}
-  }
+  };
 
   useEffect(() => {
     checkOnboarding();
@@ -213,7 +215,7 @@ const Home: React.FC<Props> = props => {
 
     return () => {
       setCanCreateProject(false);
-    }
+    };
   }, []);
 
   // Hacky legacy shim for remote cluster refresh until Context is properly split
@@ -239,7 +241,7 @@ const Home: React.FC<Props> = props => {
     } catch (error) {
       console.log(error);
     }
-  }
+  };
 
   useEffect(() => {
     getMetadata();
@@ -249,11 +251,7 @@ const Home: React.FC<Props> = props => {
         .then((isBillingEnabled) => {
           if (isBillingEnabled) {
             api
-              .getUsage(
-                "<token>",
-                {},
-                { project_id: currentProject?.id }
-              )
+              .getUsage("<token>", {}, { project_id: currentProject?.id })
               .then((res) => {
                 const usage = res.data;
                 setUsage(usage);
@@ -266,7 +264,7 @@ const Home: React.FC<Props> = props => {
         })
         .catch(console.log);
     }
-  }, [props.currentProject?.id])
+  }, [props.currentProject?.id]);
 
   useEffect(() => {
     if (
@@ -292,11 +290,7 @@ const Home: React.FC<Props> = props => {
 
   const projectOverlayCall = async () => {
     try {
-      const res = await api.getProjects(
-        "<token>",
-        {},
-        { id: user.userId }
-      );
+      const res = await api.getProjects("<token>", {}, { id: user.userId });
       if (!res.data) {
         setCurrentModal(null, null);
         return;
@@ -317,11 +311,7 @@ const Home: React.FC<Props> = props => {
   const handleDelete = async () => {
     localStorage.removeItem(currentProject.id + "-cluster");
     try {
-      await api.deleteProject(
-        "<token>",
-        {},
-        { id: currentProject?.id }
-      );
+      await api.deleteProject("<token>", {}, { id: currentProject?.id });
       projectOverlayCall();
     } catch (error) {
       console.log(error);
@@ -359,15 +349,16 @@ const Home: React.FC<Props> = props => {
   return (
     <StyledHome>
       <ModalHandler setRefreshClusters={setForceRefreshClusters} />
-      {currentOverlay && (
-        <ConfirmOverlay
-          show={true}
-          message={currentOverlay.message}
-          onYes={currentOverlay.onYes}
-          onNo={currentOverlay.onNo}
-        />
-      )}
-
+      {currentOverlay &&
+        createPortal(
+          <ConfirmOverlay
+            show={true}
+            message={currentOverlay.message}
+            onYes={currentOverlay.onYes}
+            onNo={currentOverlay.onNo}
+          />,
+          document.body
+        )}
       {/* Render sidebar when there's at least one project */}
       {projects?.length > 0 && baseRoute !== "new-project" ? (
         <Sidebar
@@ -384,7 +375,6 @@ const Home: React.FC<Props> = props => {
           Join Our Discord
         </DiscordButton>
       )}
-
       <ViewWrapper id="HomeViewWrapper">
         <Navbar
           logOut={props.logOut}
@@ -404,9 +394,10 @@ const Home: React.FC<Props> = props => {
               return <Onboarding />;
             }}
           />
-          {(user?.isPorterUser || overrideInfraTabEnabled({
-            projectID: currentProject?.id,
-          })) && (
+          {(user?.isPorterUser ||
+            overrideInfraTabEnabled({
+              projectID: currentProject?.id,
+            })) && (
             <Route
               path="/infrastructure"
               render={() => {
@@ -473,20 +464,23 @@ const Home: React.FC<Props> = props => {
           <Route path={"*"} render={() => <LaunchWrapper />} />
         </Switch>
       </ViewWrapper>
-
-      <ConfirmOverlay
-        show={currentModal === "UpdateProjectModal"}
-        message={
-          currentProject
-            ? `Are you sure you want to delete ${currentProject.name}?`
-            : ""
-        }
-        onYes={handleDelete}
-        onNo={() => setCurrentModal(null, null)}
-      />
+      {createPortal(
+        <ConfirmOverlay
+          show={currentModal === "UpdateProjectModal"}
+          message={
+            currentProject
+              ? `Are you sure you want to delete ${currentProject.name}?`
+              : ""
+          }
+          onYes={handleDelete}
+          onNo={() => setCurrentModal(null, null)}
+        />,
+        document.body
+      )}
+      ,
     </StyledHome>
   );
-}
+};
 
 export default withRouter(withAuth(Home));
 
@@ -494,9 +488,9 @@ const ViewWrapper = styled.div`
   height: 100%;
   width: 100vw;
   padding: 45px;
-  overflow-y: auto;
   display: flex;
   flex: 1;
+  overflow-y: auto;
   justify-content: center;
   background: #202227;
   position: relative;

+ 0 - 102
dashboard/src/main/home/cluster-dashboard/dashboard/Dashboard.tsx

@@ -345,99 +345,6 @@ const Br = styled.div`
   height: 35px;
 `;
 
-const RevisionHeader = styled.div`
-  color: ${(props: { showRevisions: boolean; isCurrent: boolean }) =>
-    props.isCurrent ? "#ffffff66" : "#f5cb42"};
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  height: 40px;
-  font-size: 13px;
-  width: 100%;
-  padding-left: 15px;
-  cursor: pointer;
-  :hover {
-    background: ${props => props.showRevisions && "#ffffff18"};
-    > div > i {
-      background: ${props => props.showRevisions && "#ffffff22"};
-    }
-  }
-  border-radius: 5px;
-  background: #26292e;
-  border: 1px solid #494b4f;
-  margin-top: 25px;
-  margin-bottom: 22px;
-
-  > div > i {
-    margin-left: 12px;
-    font-size: 20px;
-    cursor: pointer;
-    border-radius: 20px;
-    background: ${(props: { showRevisions: boolean; isCurrent: boolean }) =>
-      props.showRevisions ? "#ffffff18" : ""};
-    transform: ${(props: { showRevisions: boolean; isCurrent: boolean }) =>
-      props.showRevisions ? "rotate(180deg)" : ""};
-  }
-`;
-
-const Revision = styled.div`
-  color: #ffffff;
-  margin-left: 5px;
-`;
-
-const RevisionPreview = styled.div`
-  display: flex;
-  align-items: center;
-`;
-
-const DashboardIcon = styled.div`
-  height: 35px;
-  min-width: 35px;
-  width: 35px;
-  border-radius: 5px;
-  margin-right: 17px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background: #676c7c;
-  border: 2px solid #8e94aa;
-  > i {
-    font-size: 18px;
-  }
-`;
-
-const TopRow = styled.div`
-  display: flex;
-  align-items: center;
-`;
-
-const Description = styled.div`
-  color: #8b949f;
-  margin-top: 13px;
-  margin-left: 2px;
-  font-size: 13px;
-`;
-
-const InfoLabel = styled.div`
-  width: 72px;
-  height: 20px;
-  display: flex;
-  align-items: center;
-  color: #8b949f;
-  font-size: 13px;
-  > i {
-    color: #8b949f;
-    font-size: 18px;
-    margin-right: 5px;
-  }
-`;
-
-const InfoSection = styled.div`
-  margin-top: -20px;
-  font-size: 13px;
-  margin-bottom: 25px;
-`;
-
 const Url = styled.a`
   font-size: 13px;
   user-select: text;
@@ -462,12 +369,3 @@ const Bolded = styled.span`
   margin-right: 6px;
   white-space: nowrap;
 `;
-
-const FormWrapper = styled.div<{ showSave?: boolean }>`
-  width: 100%;
-  margin-top: 35px;
-  border-radius: 5px;
-  background: #26292e;
-  border: 1px solid #494b4f;
-  padding: 30px;
-`;

+ 20 - 32
dashboard/src/main/home/cluster-dashboard/dashboard/ProvisionerStatus.tsx

@@ -3,24 +3,24 @@ import styled from "styled-components";
 
 import aws from "assets/aws.png";
 import api from "shared/api";
-import loading from "assets/loading.gif";
 
 import { Context } from "shared/Context";
-import ExpandableSection from "components/porter/ExpandableSection";
 import LoadingBar from "components/porter/LoadingBar";
 import Spacer from "components/porter/Spacer";
-import Helper from "components/form-components/Helper";
 import Text from "components/porter/Text";
+import Button from "components/porter/Button";
 
 type Props = {
   provisionFailureReason: string;
 };
 
+const PROVISIONING_STATUS_POLL_INTERVAL = 60 * 1000; // poll every minute
+
 const ProvisionerStatus: React.FC<Props> = ({
   provisionFailureReason,
 }) => {
   const { currentProject, currentCluster } = useContext(Context);
-  const [progress, setProgress] = useState(1);
+  const [progress, setProgress] = useState(0);
 
   // Continuously poll provisioning status
   const pollProvisioningStatus = async () => {
@@ -33,27 +33,27 @@ const ProvisionerStatus: React.FC<Props> = ({
           cluster_id: currentCluster.id,
         }
       );
-      const { status } = res.data;
-      switch (status) {
-        case status["BOOTSTRAP_READY"]:
-          setProgress(2);
-          break;
-        case status["CONTROL_PLANE_READY"]:
-          setProgress(3);
-          break;
-        case status["INFRASTRUCTURE_READY"]:
-          setProgress(4);
-          break;
-        default:
-          setProgress(1);
+      const { is_control_plane_ready, is_infrastructure_ready, status } = res.data;
+      let progress = 1;
+      if (is_control_plane_ready) {
+        progress += 1
+      }
+      if (is_infrastructure_ready) {
+        progress += 1
+      }
+      if (status === 'Provisioned') {
+        progress += 1
       }
+      setProgress(progress);
     } catch (err) {
       console.log(err);
     }
   };
 
   useEffect(() => {
-    pollProvisioningStatus(); 
+    const intervalId = setInterval(pollProvisioningStatus, PROVISIONING_STATUS_POLL_INTERVAL);
+    pollProvisioningStatus();
+    return () => clearInterval(intervalId);
   }, []);
 
   return (
@@ -65,9 +65,9 @@ const ProvisionerStatus: React.FC<Props> = ({
         </Flex>
         <Spacer height="18px" />
         <LoadingBar
-          color={provisionFailureReason && "failed"}
+          color={provisionFailureReason ? "failed" : undefined}
           completed={progress} 
-          total={5} 
+          total={4} 
         />
         <Spacer height="18px" />
         <Text color="#aaaabb">
@@ -110,18 +110,6 @@ const Icon = styled.img`
   margin-bottom: -1px;
 `;
 
-const Img = styled.img`
-  height: 15px;
-  margin-right: 7px;
-`;
-
-const Status = styled.div`
-  color: #aaaabb;
-  display: flex;
-  align-items: center;
-  margin-left: 15px;
-`;
-
 const StyledProvisionerStatus = styled.div`
   border-radius: 5px;
   background: #26292e;

+ 11 - 7
dashboard/src/main/home/cluster-dashboard/expanded-chart/RevisionSection.tsx

@@ -12,6 +12,7 @@ import { withAuth, WithAuthProps } from "shared/auth/AuthorizationHoc";
 import Modal from "main/home/modals/Modal";
 import UpgradeChartModal from "main/home/modals/UpgradeChartModal";
 import { readableDate } from "shared/string_utils";
+import { createPortal } from "react-dom";
 
 type PropsType = WithAuthProps & {
   chart: ChartType;
@@ -352,12 +353,15 @@ class RevisionSection extends Component<PropsType, StateType> {
     return (
       <StyledRevisionSection showRevisions={this.state.expandRevisions}>
         {this.renderContents()}
-        <ConfirmOverlay
-          show={this.state.rollbackRevision && true}
-          message={`Are you sure you want to revert to version ${this.state.rollbackRevision}?`}
-          onYes={this.handleRollback}
-          onNo={() => this.setState({ rollbackRevision: null })}
-        />
+        {createPortal(
+          <ConfirmOverlay
+            show={this.state.rollbackRevision && true}
+            message={`Are you sure you want to revert to version ${this.state.rollbackRevision}?`}
+            onYes={this.handleRollback}
+            onNo={() => this.setState({ rollbackRevision: null })}
+          />,
+          document.body
+        )}
       </StyledRevisionSection>
     );
   }
@@ -473,7 +477,7 @@ const RevisionHeader = styled.div`
   padding-left: 10px;
   cursor: pointer;
   :hover {
-    background: ${props => props.showRevisions && "#ffffff18"};
+    background: ${(props) => props.showRevisions && "#ffffff18"};
   }
 
   > div > i {