Kaynağa Gözat

resolved conflicts

Justin Rhee 3 yıl önce
ebeveyn
işleme
b3923e6260

+ 1 - 1
.gitignore

@@ -21,7 +21,7 @@ openapi.yaml
 vendor
 **/*.env
 **/node_modules
-porter
+./porter
 zarf/helm/charts
 
 # Local docs directories

+ 29 - 39
Tiltfile

@@ -1,5 +1,4 @@
 load('ext://restart_process', 'docker_build_with_restart')
-load('ext://dotenv', 'dotenv')
 
 secret_settings(disable_scrub=True)
 
@@ -15,8 +14,8 @@ if config.tilt_subcommand == "down":
 
 ## Build binary locally for faster devexp
 local_resource(
-  'porter',
-  '''GOWORK=off CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -mod vendor -gcflags '-N -l' -o ./porter ./cmd/app/main.go''',
+  name='porter-binary',
+  cmd='''GOWORK=off CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -mod vendor -gcflags '-N -l' -tags ee -o ./bin/porter ./cmd/app/main.go''',
   deps=[
     "api",
     "build",
@@ -37,53 +36,45 @@ docker_build_with_restart(
     entrypoint='/app/porter',
     build_args={},
     only=[
-        "porter",
+        "bin",
     ],
     live_update=[
-        sync('./porter', '/app/'),
-    ]
+        sync('./bin/porter', '/app/'),
+        sync('./bin/migrate', '/app/'),
+    ], 
 ) 
 
 # Frontend
-# docker_build(
-#     ref="porter1/porter-dashboard",
-#     context=".",
-#     dockerfile="zarf/docker/Dockerfile.dashboard.tilt",
-#     entrypoint='webpack-dev-server --config webpack.config.js',
-#     # entrypoint='npm start',
-#     only=['dashboard/package.json', 'dashboard/package-lock.json']
-# )
-
-docker_build(
-    ref="porter1/porter-dashboard",
-    context=".",
-    dockerfile="zarf/docker/Dockerfile.dashboard.tilt",
-    build_args={'node_env': 'development'},
-    entrypoint='npm start',
-    ignore=[
-        "dashboard/node_modules",
-        "dashboard/package-lock.json",
-        "dashboard/webpack.config.js"
-    ],
-    live_update=[
-        sync('dashboard', '/app'),
-        run('cd /app && npm start', trigger=['./package.json']),
-
-        # # if all that changed was start-time.txt, make sure the server
-        # # reloads so that it will reflect the new startup time
-        # run('touch /app/index.js', trigger='./start-time.txt'),
-])
-
-dotenv(fn='zarf/helm/.dashboard.env')
-
 local_resource(
     name="porter-dashboard",
     serve_cmd="npm start",
     serve_dir="dashboard",
+    serve_env={
+        "NODE_ENV": "development",
+        "DEV_SERVER_PORT": "8081",
+        "ENABLE_PROXY": "true",
+        "API_SERVER": "http://localhost:8080"
+    },
     resource_deps=["postgresql"],
     labels=["porter"]
 )
 
+# Migrations
+local_resource(
+    name="migrations-binary",
+    cmd='''GOWORK=off CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -mod vendor -gcflags '-N -l' -tags ee -o ./bin/migrate ./cmd/migrate/main.go ./cmd/migrate/migrate_ee.go''',
+    resource_deps=["postgresql"],
+    labels=["porter"],
+)
+local_resource(
+    name="run-migrations",
+    cmd='''kubectl exec -it deploy/porter-server-web -- /app/migrate''',
+    resource_deps=["migrations-binary", "porter-binary"],
+    labels=["porter"],
+    trigger_mode=TRIGGER_MODE_MANUAL,
+)
+
+
 allow_k8s_contexts('kind-porter')
 
 cluster = str(local('kubectl config current-context')).strip()
@@ -101,7 +92,6 @@ if (cluster.startswith("kind-")):
     updated_install = encode_yaml_stream(decoded)
     k8s_yaml(updated_install)
     k8s_resource(workload='porter-server-web', port_forwards="8080:8080", labels=["porter"])
-    k8s_resource(workload='porter-dashboard', port_forwards="8081:8081", labels=["porter"], resource_deps=["postgresql"])
 else:
     local("echo 'Be careful that you aren't connected to a staging or prod cluster' && exit 1")
-    exit()
+    exit()

Dosya farkı çok büyük olduğundan ihmal edildi
+ 625 - 189
dashboard/package-lock.json


+ 115 - 25
dashboard/src/components/ProvisionerFlow.tsx

@@ -8,6 +8,14 @@ import ProvisionerForm from "components/ProvisionerForm";
 import CloudFormationForm from "components/CloudFormationForm";
 import CredentialsForm from "components/CredentialsForm";
 import Helper from "components/form-components/Helper";
+import Modal from "./porter/Modal";
+import Text from "./porter/Text";
+import Spacer from "./porter/Spacer";
+import Fieldset from "./porter/Fieldset";
+import Checkbox from "./porter/Checkbox";
+import Button from "./porter/Button";
+import ExpandableSection from "./porter/ExpandableSection";
+import Input from "./porter/Input";
 
 const providers = ["aws", "gcp", "azure"];
 
@@ -19,6 +27,8 @@ const ProvisionerFlow: React.FC<Props> = ({
   const { usage, hasBillingEnabled } = useContext(Context);
   const [currentStep, setCurrentStep] = useState("cloud");
   const [credentialId, setCredentialId] = useState("");
+  const [showCostConfirmModal, setShowCostConfirmModal] = useState(false);
+  const [confirmCost, setConfirmCost] = useState("");
 
   const isUsageExceeded = useMemo(() => {
     if (!hasBillingEnabled) {
@@ -29,31 +39,94 @@ const ProvisionerFlow: React.FC<Props> = ({
 
   if (currentStep === "cloud") {
     return (
-      <StyledProvisionerFlow>
-        <Helper>
-          Select your hosting backend:
-        </Helper>
-        <BlockList>
-          {providers.map((provider: string, i: number) => {
-            let providerInfo = integrationList[provider];
-            return (
-              <Block
-                key={i}
-                disabled={isUsageExceeded || provider === "gcp" || provider === "azure"}
-                onClick={() => {
-                  if (!(isUsageExceeded || provider === "gcp" || provider === "azure")) {
-                    setCurrentStep("credentials");
-                  }
-                }}
-              >
-                <Icon src={providerInfo.icon} />
-                <BlockTitle>{providerInfo.label}</BlockTitle>
-                <BlockDescription>{providerInfo.tagline || "Hosted in your own cloud"}</BlockDescription>
-              </Block>
-            );
-          })}
-        </BlockList>
-      </StyledProvisionerFlow>
+      <>
+        <StyledProvisionerFlow>
+          <Helper>
+            Select your hosting backend:
+          </Helper>
+          <BlockList>
+            {providers.map((provider: string, i: number) => {
+              let providerInfo = integrationList[provider];
+              return (
+                <Block
+                  key={i}
+                  disabled={isUsageExceeded || provider === "gcp" || provider === "azure"}
+                  onClick={() => {
+                    if (!(isUsageExceeded || provider === "gcp" || provider === "azure")) {
+                      setShowCostConfirmModal(true);
+                    }
+                  }}
+                >
+                  <Icon src={providerInfo.icon} />
+                  <BlockTitle>{providerInfo.label}</BlockTitle>
+                  <BlockDescription>{providerInfo.tagline || "Hosted in your own cloud"}</BlockDescription>
+                </Block>
+              );
+            })}
+          </BlockList>
+        </StyledProvisionerFlow>
+        {showCostConfirmModal && (
+          <Modal closeModal={() => {
+            setConfirmCost("");
+            setShowCostConfirmModal(false);
+          }}>
+            <Text size={16} weight={500}>
+              AWS base cost consent
+            </Text>
+            <Spacer height="15px" />
+            <Text color="helper">
+              Porter will create resources in your existing AWS account for hosting your applications. You will be separately charged by AWS and can use your cloud credits.
+            </Text>
+            <Spacer y={1} />
+            <Text color="helper">
+              AWS base cost before cloud credits:
+            </Text>
+            <Spacer y={1} />
+            <ExpandableSection
+              background="#ffffff11"
+              Header={
+                <Cost>$315.94 / mo</Cost>
+              }
+              ExpandedSection={
+                <Dark>
+                  <Text>
+                    • Amazon Elastic Kubernetes Service (EKS) = $73/mo
+                    <Spacer height="15px" />
+                    • Amazon EC2:
+                    <Spacer height="15px" />
+                    <Tab />+ System workloads: t3.medium instance (2) = $60.74/mo
+                    <Spacer height="15px" />
+                    <Tab />+ Monitoring workloads: t3.large instance (1) = $60.74/mo
+                    <Spacer height="15px" />
+                    <Tab />+ Application workloads: t3.xlarge instance (1) = $121.47/mo
+                  </Text>
+                </Dark>
+              }
+            />
+            <Spacer y={1} />
+            <Text color="helper">
+              Porter metered cost: $0.019/hr/vCPU + $0.009/hr/GB RAM.
+            </Text>
+            <Spacer y={1} />
+            <Text color="helper">
+              All AWS resources will be automatically deleted when you delete your Porter project. Please enter the base cost ("315.94") below to proceed:
+            </Text>
+            <Spacer y={1} />
+            <Input placeholder="315.94" value={confirmCost} setValue={setConfirmCost} width="100%" height="40px" />
+            <Spacer y={1} />
+            <Button
+              disabled={confirmCost !== "315.94"}
+              onClick={() => {
+                setShowCostConfirmModal(false);
+                setConfirmCost("");
+                setCurrentStep("credentials");
+              }}
+            >
+              Continue
+            </Button>
+          </Modal>
+        )}
+      </>
     );
   } else if (currentStep === "credentials") {
     return (
@@ -77,6 +150,23 @@ const ProvisionerFlow: React.FC<Props> = ({
 
 export default ProvisionerFlow;
 
+const Dark = styled.div`
+  position: relative;
+  padding: 25px;
+  background: #1b1d2688;
+  font-size: 13px;
+`;
+
+const Cost = styled.div`
+  font-weight: 600;
+  font-size: 20px;
+`;
+
+const Tab = styled.span`
+  margin-left: 20px;
+  height: 1px;
+`;
+
 const BlockList = styled.div`
   overflow: visible;
   margin-top: 25px;

+ 52 - 0
dashboard/src/components/porter/Checkbox.tsx

@@ -0,0 +1,52 @@
+import React, { useEffect, useState } from "react";
+import styled from "styled-components";
+
+type Props = {
+  checked: boolean;
+  toggleChecked: () => void;
+  children: React.ReactNode;
+};
+
+const Checkbox: React.FC<Props> = ({
+  checked,
+  toggleChecked,
+  children,
+}) => {
+  return (
+    <StyledCheckbox>
+      <Box 
+        checked={checked}
+        onClick={toggleChecked}
+      >
+        <i className="material-icons">done</i>
+      </Box>
+      {children}
+    </StyledCheckbox>
+  );
+};
+
+export default Checkbox;
+
+const StyledCheckbox = styled.div`
+  display: flex;
+  align-items: center;
+`;
+
+const Box = styled.div<{ checked: boolean }>`
+  width: 12px;
+  height: 12px;
+  cursor: pointer;
+  border: 1px solid #ffffff55;
+  margin-right: 10px;
+  border-radius: 3px;
+  background: ${(props) => (props.checked ? "#ffffff22" : "#ffffff11")};
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  > i {
+    font-size: 12px;
+    padding-left: 0px;
+    display: ${(props) => (props.checked ? "" : "none")};
+  }
+`;

+ 11 - 3
dashboard/src/components/porter/ExpandableSection.tsx

@@ -6,6 +6,7 @@ type Props = {
   Header: any;
   ExpandedSection: any;
   color?: any;
+  background?: string;
 };
 
 const ExpandableSection: React.FC<Props> = ({
@@ -13,6 +14,7 @@ const ExpandableSection: React.FC<Props> = ({
   Header,
   ExpandedSection,
   color,
+  background,
 }) => {
   const [isExpanded, setIsExpanded] = useState(false);
   useEffect(() => {
@@ -20,7 +22,10 @@ const ExpandableSection: React.FC<Props> = ({
   }, [isInitiallyExpanded]);
 
   return (
-    <StyledExpandableSection isExpanded={isExpanded}>
+    <StyledExpandableSection 
+      isExpanded={isExpanded}
+      background={background}
+    >
       <HeaderRow 
         isExpanded={isExpanded}
         onClick={() => setIsExpanded(!isExpanded)}
@@ -66,13 +71,16 @@ const HeaderRow = styled.div<{
   }
 `;
 
-const StyledExpandableSection = styled.div<{ isExpanded: boolean }>`
+const StyledExpandableSection = styled.div<{ 
+  isExpanded: boolean;
+  background?: string;
+}>`
   width: 100%;
   height: ${props => props.isExpanded ? "" : "40px"};
   max-height: 255px;
   overflow: hidden;
   border-radius: 5px;
-  background: #26292e;
+  background: ${props => props.background || "#26292e"};
   border: 1px solid #494b4f;
   :hover {
     border: 1px solid #7a7b80;

+ 39 - 0
dashboard/src/components/porter/Fieldset.tsx

@@ -0,0 +1,39 @@
+import React, { useEffect, useState } from "react";
+import styled from "styled-components";
+
+type Props = {
+  children: React.ReactNode;
+  background?: string;
+};
+
+const Fieldset: React.FC<Props> = ({
+  children,
+  background,
+}) => {
+  const getBackground = () => {
+    switch (background) {
+      case "dark":
+        return "#1b1d2688";
+      default:
+        return background;
+    }
+  };
+  return (
+    <StyledFieldset background={getBackground()}>
+      {children}
+    </StyledFieldset>
+  );
+};
+
+export default Fieldset;
+
+const StyledFieldset = styled.div<{
+  background?: string;
+}>`
+  position: relative;
+  padding: 25px;
+  border-radius: 5px;
+  background: ${props => props.background || "#26292e"};
+  border: 1px solid #494b4f;
+  font-size: 13px;
+`;

+ 99 - 0
dashboard/src/components/porter/Modal.tsx

@@ -0,0 +1,99 @@
+import React, { useEffect, useState } from "react";
+import styled from "styled-components";
+
+type Props = {
+  closeModal?: () => void;
+  children: React.ReactNode;
+};
+
+const Modal: React.FC<Props> = ({
+  closeModal,
+  children,
+}) => {
+  return (
+    <ModalWrapper>
+      <ModalBg onClick={closeModal} />
+      <StyledModal> 
+        {closeModal && (
+          <CloseButton onClick={closeModal}>
+            <i className="material-icons">close</i>
+          </CloseButton>
+        )}
+        {children}
+      </StyledModal>
+    </ModalWrapper>
+  );
+};
+
+export default Modal;
+
+const CloseButton = styled.div`
+  position: absolute;
+  display: block;
+  width: 40px;
+  height: 40px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 1;
+  border-radius: 50%;
+  right: 12px;
+  top: 10px;
+  cursor: pointer;
+  :hover {
+    background-color: #ffffff11;
+  }
+
+  > i {
+    font-size: 20px;
+    color: #aaaabb;
+  }
+`;
+
+const ModalWrapper = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: fixed;
+  margin: 0;
+  padding: 0;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 100vh;
+  z-index: 100;
+`;
+
+const ModalBg = styled.div`
+  position: fixed;
+  margin: 0;
+  padding: 0;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 100vh;
+  background-color: rgba(0, 0, 0, 0.6);
+`;
+
+const StyledModal = styled.div`
+  position: relative;
+  padding: 25px;
+  border-radius: 10px;
+  border: 1px solid #494b4f;
+  font-size: 13px;
+  width: 600px;
+  background: #42444944;
+  backdrop-filter: saturate(150%) blur(10px);
+
+  animation: floatInModal 0.5s 0s;
+  @keyframes floatInModal {
+    from {
+      opacity: 0;
+      transform: translateY(30px);
+    }
+    to {
+      opacity: 1;
+      transform: translateY(0px);
+    }
+  }
+`;

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

@@ -4,11 +4,13 @@ import styled from "styled-components";
 type Props = {
   size?: number;
   color?: string;
+  weight?: number;
   children: any;
 };
 
 const Text: React.FC<Props> = ({
   size,
+  weight,
   color,
   children
 }) => {
@@ -25,6 +27,7 @@ const Text: React.FC<Props> = ({
     <StyledText
       size={size}
       color={getColor()}
+      weight={weight}
     >
       {children}
     </StyledText>
@@ -35,8 +38,11 @@ export default Text;
 
 const StyledText = styled.div<{ 
   size?: number; 
-  color?: string 
+  color?: string;
+  weight?: number;
 }>`
+  line-height: 1.5;
+  font-weight: ${props => props.weight || 400};
   color: ${props => props.color || "#ffffff"};
   font-size: ${props => props.size || 13}px;
 `;

+ 5 - 3
dashboard/src/main/home/dashboard/ClusterSection.tsx

@@ -93,9 +93,11 @@ const ClusterSection = (props: Props) => {
   }
   return (
     <>
-      <Button onClick={() => setCurrentStep("cloud")}>
-        <i className="material-icons">add</i> Create a cluster
-      </Button>
+      {usage?.current.cluster > 1 && (
+        <Button onClick={() => setCurrentStep("cloud")}>
+          <i className="material-icons">add</i> Create a cluster
+        </Button>
+      )}
       <ClusterList />
     </>
   );

+ 1 - 1
zarf/docker/Dockerfile.server.tilt

@@ -7,4 +7,4 @@ FROM debian:bullseye-slim as runner
 WORKDIR /app
 COPY --from=installer /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
 COPY --from=installer /go/bin/dlv /
-COPY ./porter /app
+COPY ./bin/porter /app

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor