Просмотр исходного кода

Merge branch 'stacks-v1' of github.com:porter-dev/porter into stacks-v1

Soham Dessai 3 лет назад
Родитель
Сommit
74f74b14fc

+ 1 - 1
dashboard/src/components/porter/ExpandableSection.tsx

@@ -101,7 +101,7 @@ const StyledExpandableSection = styled.div<{
 }>`
   width: 100%;
   height: ${props => (props.isExpanded || props.noWrapper) ? "" : "40px"};
-  max-height: 300px;
+  max-height: 350px;
   overflow: hidden;
   border-radius: 5px;
   background: ${props => !props.noWrapper && (props.background || "#26292e")};

+ 14 - 19
dashboard/src/main/home/app-dashboard/new-app-flow/GithubActionModal.tsx

@@ -8,6 +8,11 @@ import styled from "styled-components";
 import Button from "components/porter/Button";
 import Select from "components/porter/Select";
 import api from "shared/api";
+import { CopyBlock } from "react-code-blocks";
+import { getGithubAction } from "./utils";
+import AceEditor from "react-ace";
+import YamlEditor from "components/YamlEditor";
+
 
 interface GithubActionModalProps {
   closeModal: () => void;
@@ -79,7 +84,7 @@ const GithubActionModal: React.FC<GithubActionModalProps> = ({
       </Text>
       <Spacer height="15px" />
       <Text color="helper">
-        In order to automatically update your services every time new code is pushed to your GitHub branch, the following file must exist in your Github repository:
+        In order to automatically update your services every time new code is pushed to your GitHub branch, the following file must exist in your GitHub repository:
       </Text>
       <Spacer y={1} />
       <ExpandableSection
@@ -87,24 +92,15 @@ const GithubActionModal: React.FC<GithubActionModalProps> = ({
         expandText="[+] Show code"
         collapseText="[-] Hide code"
         Header={
-          <ModalHeader>./github/workflows/porter_deploy.yml</ModalHeader>
+          <ModalHeader>.github/workflows/porter.yml</ModalHeader>
         }
         isInitiallyExpanded={true}
         ExpandedSection={
-          <>
-            <Spacer height="15px" />
-            <Fieldset background="#1b1d2688">
-              • 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
-            </Fieldset>
-          </>
+          <YamlEditor
+            value={getGithubAction(projectId, stackName)}
+            readOnly={true}
+            height="300px"
+          />
         }
       />
       <Spacer y={1} />
@@ -114,7 +110,7 @@ const GithubActionModal: React.FC<GithubActionModalProps> = ({
       <Spacer y={1} />
       <Select
         options={[
-          { label: "I authorize Porter to open a PR on my behalf", value: "open_pr" },
+          { label: "I authorize Porter to open a PR on my behalf (recommended)", value: "open_pr" },
           { label: "I will copy the file into my repository myself", value: "copy" },
         ]}
         setValue={(x: string) => setChoice(x as Choice)}
@@ -141,7 +137,6 @@ const Tab = styled.span`
 
 const ModalHeader = styled.div`
   font-weight: 600;
-  font-size: 20px;
+  font-size: 1.5vw;
   font-family: monospace; ;
-
 `;

+ 62 - 46
dashboard/src/main/home/app-dashboard/new-app-flow/NewAppFlow.tsx

@@ -77,6 +77,11 @@ const Validators: {
   releaseCommand: (value: string) => true,
 };
 
+type Detected = {
+  detected: boolean;
+  message: string;
+}
+
 const NewAppFlow: React.FC<Props> = ({ ...props }) => {
   const [templateName, setTemplateName] = useState("");
 
@@ -101,6 +106,7 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
   const [porterJson, setPorterJson] = useState<
     z.infer<typeof PorterYamlSchema>
   >(null);
+  const [detected, setDetected] = useState<Detected | undefined>(undefined);
 
   const validatePorterYaml = (yamlString: string) => {
     let parsedYaml;
@@ -109,7 +115,6 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
       const parsedData = PorterYamlSchema.parse(parsedYaml);
       const porterYamlToJson = parsedData as z.infer<typeof PorterYamlSchema>;
       setPorterJson(porterYamlToJson);
-      console.log(porterYamlToJson);
       // go through key value pairs and create services from them, if they don't already exist
       const newServices = [];
       const existingServices = formState.serviceList.map((s) => s.name);
@@ -141,6 +146,13 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
       if (Validators.serviceList(newServiceList)) {
         setCurrentStep(Math.max(currentStep, 4));
       }
+      if (porterYamlToJson &&
+        porterYamlToJson.apps &&
+        Object.keys(porterYamlToJson.apps).length > 0) {
+        setDetected({ detected: true, message: `Detected ${Object.keys(porterYamlToJson.apps).length} apps from porter.yaml` });
+      } else {
+        setDetected({ detected: false, message: "Could not detect any apps from porter.yaml. Make sure it exists in the root of your repo." });
+      }
     } catch (error) {
       console.log("Error converting porter yaml file to input: " + error);
     }
@@ -171,46 +183,51 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
   };
   const deployPorterApp = async () => {
     try {
-      // Write build settings to the DB
-      const res = await api.createPorterApp(
-        "<token>",
-        {
-          name: formState.applicationName,
-          repo_name: actionConfig.git_repo,
-          git_branch: branch,
-          build_context: folderPath,
-          builder: (buildConfig as any)?.builder,
-          buildpacks: (buildConfig as any)?.buildpacks.join(",") ?? "",
-          dockerfile: dockerfilePath,
-          image_repo_uri: imageUrl,
-        },
-        {
-          cluster_id: currentCluster.id,
-          project_id: currentProject.id,
-        }
-      );
-
       const finalPorterYaml = createFinalPorterYaml();
       const yamlString = yaml.dump(finalPorterYaml);
       const base64Encoded = btoa(yamlString);
-
-      //create dummy chart
-      await api.updatePorterStack(
-        "<token>",
-        {
-          stack_name: formState.applicationName,
-          porter_yaml: base64Encoded,
-        },
-        {
-          cluster_id: currentCluster.id,
-          project_id: currentProject.id,
+      const imageInfo = imageUrl ? {
+        image_info: {
+          repository: imageUrl,
+          tag: imageTag,
         }
-      );
+      } : {}
+
+      // only deploy + write to DB if we can create a final porter yaml
+      await Promise.all([
+        api.createPorterApp(
+          "<token>",
+          {
+            name: formState.applicationName,
+            repo_name: actionConfig.git_repo,
+            git_branch: branch,
+            build_context: folderPath,
+            builder: (buildConfig as any)?.builder,
+            buildpacks: (buildConfig as any)?.buildpacks.join(",") ?? "",
+            dockerfile: dockerfilePath,
+            image_repo_uri: imageUrl,
+          },
+          {
+            cluster_id: currentCluster.id,
+            project_id: currentProject.id,
+          }
+        ),
+        api.updatePorterStack(
+          "<token>",
+          {
+            stack_name: formState.applicationName,
+            porter_yaml: base64Encoded,
+            ...imageInfo,
+          },
+          {
+            cluster_id: currentCluster.id,
+            project_id: currentProject.id,
+          }
+        ),
+      ])
     } catch (err) {
       console.log(err);
     }
-
-    // TODO: update Porter stack
   };
 
   const combineEnv = (
@@ -343,19 +360,15 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
                 />
               </>,
               <>
-                <Text size={16}>Application services</Text>
+                <Text size={16}>Application services {detected && (
+                  <AppearingDiv>
+                    <Text size={16} color={detected.detected ? "green" : "red"}>
+                      {detected.detected ? <i className="material-icons">check</i> : <i className="material-icons">error</i>} {detected.message}
+                    </Text>
+                  </AppearingDiv>
+                )}</Text>
                 <Spacer y={0.5} />
-                {porterJson &&
-                  porterJson.apps &&
-                  Object.keys(porterJson.apps).length > 0 && (
-                    <AppearingDiv>
-                      <Text size={16} color={"green"}>
-                        Auto-detected {Object.keys(porterJson.apps).length}{" "}
-                        services from porter.yaml!
-                      </Text>
-                      <Spacer y={1} />
-                    </AppearingDiv>
-                  )}
+
                 <Services
                   setServices={(services: any[]) => {
                     setFormState({ ...formState, serviceList: services });
@@ -475,6 +488,9 @@ const Icon = styled.img`
 const AppearingDiv = styled.div`
   animation: floatIn 0.5s;
   animation-fill-mode: forwards;
+  display: flex;
+  align-items: center;
+  margin-left: 10px;
   @keyframes floatIn {
     from {
       opacity: 0;

+ 6 - 6
dashboard/src/main/home/app-dashboard/new-app-flow/serviceTypes.ts

@@ -26,8 +26,8 @@ export type WorkerService = SharedServiceParams & {
 const WorkerService = {
     default: (name: string, startCommand: ServiceReadOnlyField): WorkerService => ({
         name,
-        cpu: '',
-        ram: '',
+        cpu: '100',
+        ram: '256',
         startCommand: startCommand,
         type: 'worker',
         replicas: '1',
@@ -69,8 +69,8 @@ export type WebService = SharedServiceParams & Omit<WorkerService, 'type'> & {
 const WebService = {
     default: (name: string, startCommand: ServiceReadOnlyField): WebService => ({
         name,
-        cpu: '',
-        ram: '',
+        cpu: '100',
+        ram: '256',
         startCommand: startCommand,
         type: 'web',
         replicas: '1',
@@ -128,8 +128,8 @@ export type JobService = SharedServiceParams & {
 const JobService = {
     default: (name: string, startCommand: ServiceReadOnlyField): JobService => ({
         name,
-        cpu: '',
-        ram: '',
+        cpu: '100',
+        ram: '256',
         startCommand: startCommand,
         type: 'job',
         jobsExecuteConcurrently: false,

+ 29 - 0
dashboard/src/main/home/app-dashboard/new-app-flow/utils.tsx

@@ -14,3 +14,32 @@ export const overrideObjectValues = (obj1: any, obj2: any) => {
     // Return the merged object
     return obj1;
 };
+
+export const getGithubAction = (projectID?: number, stackName?: string) => {
+    return `on:
+  push:
+    branches:
+    - master
+name: Deploy to Porter
+jobs:
+  porter-deploy:
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout code
+      uses: actions/checkout@v3
+    - name: Set Github tag
+      id: vars
+      run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
+    - name: Deploy stack
+      timeout-minutes: 30
+      uses: porter-dev/porter-cli-action@v0.1.0
+      with:
+        command: apply -f porter.yaml
+      env:
+        PORTER_CLUSTER: "1"
+        PORTER_HOST: https://296e-160-72-72-58.ngrok-free.app
+        PORTER_PROJECT: "${projectID}"
+        PORTER_STACK_NAME: "${stackName}"
+        PORTER_TAG: \${{ steps.vars.outputs.sha_short }}
+        PORTER_TOKEN: \${{ secrets.PORTER_STACK_1_1 }}`;
+}

+ 4 - 0
dashboard/src/shared/api.tsx

@@ -200,6 +200,10 @@ const updatePorterStack = baseApi<
   {
     stack_name: string;
     porter_yaml: string;
+    image_info?: {
+      repository: string;
+      tag: string;
+    }
   },
   {
     project_id: number;