Soham Dessai vor 3 Jahren
Ursprung
Commit
e01e660eec

+ 191 - 48
dashboard/src/main/home/app-dashboard/new-app-flow/WebTabs.tsx

@@ -1,5 +1,5 @@
 import Input from "components/porter/Input";
-import React, { useEffect } from "react"
+import React, { useEffect } from "react";
 import Text from "components/porter/Text";
 import Spacer from "components/porter/Spacer";
 import TabSelector from "components/TabSelector";
@@ -8,17 +8,13 @@ import { WebService } from "./serviceTypes";
 import { Height } from "react-animate-height";
 
 interface Props {
-  service: WebService
-  editService: (service: WebService) => void
-  setHeight: (height: Height) => void
+  service: WebService;
+  editService: (service: WebService) => void;
+  setHeight: (height: Height) => void;
 }
 
-const WebTabs: React.FC<Props> = ({
-  service,
-  editService,
-  setHeight,
-}) => {
-  const [currentTab, setCurrentTab] = React.useState<string>('main');
+const WebTabs: React.FC<Props> = ({ service, editService, setHeight }) => {
+  const [currentTab, setCurrentTab] = React.useState<string>("main");
 
   const renderMain = () => {
     return (
@@ -43,7 +39,12 @@ const WebTabs: React.FC<Props> = ({
           value={service.startCommand.value}
           width="300px"
           disabled={service.startCommand.readOnly}
-          setValue={(e) => { editService({ ...service, startCommand: { readOnly: false, value: e } }) }}
+          setValue={(e) => {
+            editService({
+              ...service,
+              startCommand: { readOnly: false, value: e },
+            });
+          }}
           disabledTooltip={"You may only edit this field in your porter.yaml."}
         />
         <Spacer y={1} />
@@ -53,20 +54,33 @@ const WebTabs: React.FC<Props> = ({
           value={service.port.value}
           disabled={service.port.readOnly}
           width="300px"
-          setValue={(e) => { editService({ ...service, port: { readOnly: false, value: e } }) }}
+          setValue={(e) => {
+            editService({ ...service, port: { readOnly: false, value: e } });
+          }}
           disabledTooltip={"You may only edit this field in your porter.yaml."}
         />
         <Spacer y={1} />
         <Checkbox
           checked={service.ingress.enabled.value}
           disabled={service.ingress.enabled.readOnly}
-          toggleChecked={() => { editService({ ...service, ingress: { ...service.ingress, enabled: { readOnly: false, value: !service.ingress.enabled.value } } }) }}
+          toggleChecked={() => {
+            editService({
+              ...service,
+              ingress: {
+                ...service.ingress,
+                enabled: {
+                  readOnly: false,
+                  value: !service.ingress.enabled.value,
+                },
+              },
+            });
+          }}
           disabledTooltip={"You may only edit this field in your porter.yaml."}
         >
           <Text color="helper">Generate a Porter URL for external traffic</Text>
         </Checkbox>
       </>
-    )
+    );
   };
 
   const renderResources = () => {
@@ -79,7 +93,9 @@ const WebTabs: React.FC<Props> = ({
           value={service.cpu.value}
           disabled={service.cpu.readOnly}
           width="300px"
-          setValue={(e) => { editService({ ...service, cpu: { readOnly: false, value: e } }) }}
+          setValue={(e) => {
+            editService({ ...service, cpu: { readOnly: false, value: e } });
+          }}
           disabledTooltip={"You may only edit this field in your porter.yaml."}
         />
         <Spacer y={1} />
@@ -89,7 +105,9 @@ const WebTabs: React.FC<Props> = ({
           value={service.ram.value}
           disabled={service.ram.readOnly}
           width="300px"
-          setValue={(e) => { editService({ ...service, ram: { readOnly: false, value: e } }) }}
+          setValue={(e) => {
+            editService({ ...service, ram: { readOnly: false, value: e } });
+          }}
           disabledTooltip={"You may only edit this field in your porter.yaml."}
         />
         <Spacer y={1} />
@@ -97,15 +115,37 @@ const WebTabs: React.FC<Props> = ({
           label="Replicas"
           placeholder="ex: 1"
           value={service.replicas.value}
-          disabled={service.replicas.readOnly || service.autoscaling.enabled.value}
+          disabled={
+            service.replicas.readOnly || service.autoscaling.enabled.value
+          }
           width="300px"
-          setValue={(e) => { editService({ ...service, replicas: { readOnly: false, value: e } }) }}
-          disabledTooltip={service.replicas.readOnly ? "You may only edit this field in your porter.yaml." : "Disable autoscaling to specify replicas."}
+          setValue={(e) => {
+            editService({
+              ...service,
+              replicas: { readOnly: false, value: e },
+            });
+          }}
+          disabledTooltip={
+            service.replicas.readOnly
+              ? "You may only edit this field in your porter.yaml."
+              : "Disable autoscaling to specify replicas."
+          }
         />
         <Spacer y={1} />
         <Checkbox
           checked={service.autoscaling.enabled.value}
-          toggleChecked={() => { editService({ ...service, autoscaling: { ...service.autoscaling, enabled: { readOnly: false, value: !service.autoscaling.enabled.value } } }) }}
+          toggleChecked={() => {
+            editService({
+              ...service,
+              autoscaling: {
+                ...service.autoscaling,
+                enabled: {
+                  readOnly: false,
+                  value: !service.autoscaling.enabled.value,
+                },
+              },
+            });
+          }}
           disabled={service.autoscaling.enabled.readOnly}
           disabledTooltip={"You may only edit this field in your porter.yaml."}
         >
@@ -116,43 +156,134 @@ const WebTabs: React.FC<Props> = ({
           label="Min replicas"
           placeholder="ex: 1"
           value={service.autoscaling.minReplicas.value}
-          disabled={service.autoscaling.minReplicas.readOnly || !service.autoscaling.enabled.value}
+          disabled={
+            service.autoscaling.minReplicas.readOnly ||
+            !service.autoscaling.enabled.value
+          }
           width="300px"
-          setValue={(e) => { editService({ ...service, autoscaling: { ...service.autoscaling, minReplicas: { readOnly: false, value: e } } }) }}
-          disabledTooltip={service.autoscaling.minReplicas.readOnly ? "You may only edit this field in your porter.yaml." : "Enable autoscaling to specify min replicas."}
+          setValue={(e) => {
+            editService({
+              ...service,
+              autoscaling: {
+                ...service.autoscaling,
+                minReplicas: { readOnly: false, value: e },
+              },
+            });
+          }}
+          disabledTooltip={
+            service.autoscaling.minReplicas.readOnly
+              ? "You may only edit this field in your porter.yaml."
+              : "Enable autoscaling to specify min replicas."
+          }
         />
         <Spacer y={1} />
         <Input
           label="Max replicas"
           placeholder="ex: 10"
           value={service.autoscaling.maxReplicas.value}
-          disabled={service.autoscaling.maxReplicas.readOnly || !service.autoscaling.enabled.value}
+          disabled={
+            service.autoscaling.maxReplicas.readOnly ||
+            !service.autoscaling.enabled.value
+          }
           width="300px"
-          setValue={(e) => { editService({ ...service, autoscaling: { ...service.autoscaling, maxReplicas: { readOnly: false, value: e } } }) }}
-          disabledTooltip={service.autoscaling.maxReplicas.readOnly ? "You may only edit this field in your porter.yaml." : "Enable autoscaling to specify max replicas."}
+          setValue={(e) => {
+            editService({
+              ...service,
+              autoscaling: {
+                ...service.autoscaling,
+                maxReplicas: { readOnly: false, value: e },
+              },
+            });
+          }}
+          disabledTooltip={
+            service.autoscaling.maxReplicas.readOnly
+              ? "You may only edit this field in your porter.yaml."
+              : "Enable autoscaling to specify max replicas."
+          }
         />
         <Spacer y={1} />
         <Input
           label="Target CPU utilization (%)"
           placeholder="ex: 50"
           value={service.autoscaling.targetCPUUtilizationPercentage.value}
-          disabled={service.autoscaling.targetCPUUtilizationPercentage.readOnly || !service.autoscaling.enabled.value}
+          disabled={
+            service.autoscaling.targetCPUUtilizationPercentage.readOnly ||
+            !service.autoscaling.enabled.value
+          }
           width="300px"
-          setValue={(e) => { editService({ ...service, autoscaling: { ...service.autoscaling, targetCPUUtilizationPercentage: { readOnly: false, value: e } } }) }}
-          disabledTooltip={service.autoscaling.targetCPUUtilizationPercentage.readOnly ? "You may only edit this field in your porter.yaml." : "Enable autoscaling to specify target CPU utilization."}
+          setValue={(e) => {
+            editService({
+              ...service,
+              autoscaling: {
+                ...service.autoscaling,
+                targetCPUUtilizationPercentage: { readOnly: false, value: e },
+              },
+            });
+          }}
+          disabledTooltip={
+            service.autoscaling.targetCPUUtilizationPercentage.readOnly
+              ? "You may only edit this field in your porter.yaml."
+              : "Enable autoscaling to specify target CPU utilization."
+          }
         />
         <Spacer y={1} />
         <Input
           label="Target RAM utilization (%)"
           placeholder="ex: 50"
           value={service.autoscaling.targetMemoryUtilizationPercentage.value}
-          disabled={service.autoscaling.targetMemoryUtilizationPercentage.readOnly || !service.autoscaling.enabled.value}
+          disabled={
+            service.autoscaling.targetMemoryUtilizationPercentage.readOnly ||
+            !service.autoscaling.enabled.value
+          }
           width="300px"
-          setValue={(e) => { editService({ ...service, autoscaling: { ...service.autoscaling, targetMemoryUtilizationPercentage: { readOnly: false, value: e } } }) }}
-          disabledTooltip={service.autoscaling.targetMemoryUtilizationPercentage.readOnly ? "You may only edit this field in your porter.yaml." : "Enable autoscaling to specify target RAM utilization."}
+          setValue={(e) => {
+            editService({
+              ...service,
+              autoscaling: {
+                ...service.autoscaling,
+                targetMemoryUtilizationPercentage: {
+                  readOnly: false,
+                  value: e,
+                },
+              },
+            });
+          }}
+          disabledTooltip={
+            service.autoscaling.targetMemoryUtilizationPercentage.readOnly
+              ? "You may only edit this field in your porter.yaml."
+              : "Enable autoscaling to specify target RAM utilization."
+          }
         />
       </>
-    )
+    );
+  };
+
+  const renderHealth = () => {
+    return (
+      <>
+        <Spacer y={1} />
+        <Checkbox
+          checked={service.health.livenessProbe.enabled.value}
+          toggleChecked={() => {
+            editService({
+              ...service,
+              health: {
+                livenessProbe: {
+                  enabled: {
+                    readOnly: false,
+                    value: !service.health.livenessProbe.enabled.value,
+                  },
+                },
+              },
+            });
+          }}
+          //disabled={service.autoscaling.enabled.readOnly}
+          //disabledTooltip={"You may only edit this field in your porter.yaml."}
+        >
+          <Text color="helper">Enable Liveness</Text>
+        </Checkbox>
+      </>
+    );
   };
 
   const renderAdvanced = () => {
@@ -169,43 +300,55 @@ const WebTabs: React.FC<Props> = ({
               >
                 &nbsp;(?)
               </a>
-            </>}
+            </>
+          }
           placeholder="ex: my-app.my-domain.com"
           value={service.ingress.hosts.value}
           disabled={service.ingress.hosts.readOnly}
           width="300px"
-          setValue={(e) => { editService({ ...service, ingress: { ...service.ingress, hosts: { readOnly: false, value: e } } }) }}
+          setValue={(e) => {
+            editService({
+              ...service,
+              ingress: {
+                ...service.ingress,
+                hosts: { readOnly: false, value: e },
+              },
+            });
+          }}
           disabledTooltip={"You may only edit this field in your porter.yaml."}
         />
       </>
     );
   };
-
   return (
     <>
       <TabSelector
         options={[
-          { label: 'Main', value: 'main' },
-          { label: 'Resources', value: 'resources' },
-          { label: 'Advanced', value: 'advanced' },
+          { label: "Main", value: "main" },
+          { label: "Resources", value: "resources" },
+          { label: "Advanced", value: "advanced" },
+          { label: "Health", value: "health" },
         ]}
         currentTab={currentTab}
         setCurrentTab={(value: string) => {
-          if (value === 'main') {
+          if (value === "main") {
             setHeight(288);
-          } else if (value === 'resources') {
+          } else if (value === "resources") {
             setHeight(713);
-          } else if (value === 'advanced') {
+          } else if (value === "advanced") {
+            setHeight(159);
+          } else if (value === "health") {
             setHeight(159);
           }
           setCurrentTab(value);
         }}
       />
-      {currentTab === 'main' && renderMain()}
-      {currentTab === 'resources' && renderResources()}
-      {currentTab === 'advanced' && renderAdvanced()}
+      {currentTab === "main" && renderMain()}
+      {currentTab === "resources" && renderResources()}
+      {currentTab === "advanced" && renderAdvanced()}
+      {currentTab === "health" && renderHealth()}
     </>
-  )
-}
+  );
+};
 
-export default WebTabs;
+export default WebTabs;

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

@@ -28,6 +28,109 @@ type Autoscaling = {
     targetMemoryUtilizationPercentage: ServiceString,
 }
 
+// health:
+//   livenessCommand:
+//     command: ls -l
+//     enabled: false
+//     failureThreshold: 3
+//     initialDelaySeconds: 5
+//     periodSeconds: 5
+//     successThreshold: 1
+//     timeoutSeconds: 1
+//   livenessProbe:
+//     auth:
+//       enabled: false
+//       password: ''
+//       username: ''
+//     enabled: false
+//     failureThreshold: 3
+//     initialDelaySeconds: 0
+//     path: /livez
+//     periodSeconds: 5
+//     scheme: HTTP
+//     successThreshold: 1
+//     timeoutSeconds: 1
+//   readinessProbe:
+//     auth:
+//       enabled: false
+//       password: ''
+//       username: ''
+//     enabled: false
+//     failureThreshold: 3
+//     initialDelaySeconds: 0
+//     path: /readyz
+//     periodSeconds: 5
+//     scheme: HTTP
+//     successThreshold: 1
+//     timeoutSeconds: 1
+//   startupProbe:
+//     auth:
+//       enabled: false
+//       password: ''
+//       username: ''
+//     enabled: false
+//     failureThreshold: 3
+//     path: /startupz
+//     periodSeconds: 5
+//     scheme: HTTP
+//     timeoutSeconds: 1
+type livenessCommand = {
+    command: ServiceString,
+    enabled: ServiceBoolean,
+    failureThreshold: ServiceNum,
+    initialDelaySeconds: ServiceNum,
+    periodSeconds: ServiceNum,
+    successThreshold: ServiceNum,
+    timeoutSeconds: ServiceNum,
+}
+type Auth ={
+    enabled: ServiceBoolean,
+    password: ServiceString,
+    username: ServiceString,
+}
+type  LivenessProbe = {
+    enabled: ServiceBoolean,
+    failureThreshold: ServiceString,
+    initialDelaySeconds: ServiceString,
+    path: ServiceString,
+    periodSeconds: ServiceString,
+    scheme: ServiceString,
+    successThreshold: ServiceString,
+    timeoutSeconds: ServiceString,
+    auth: Auth,
+}
+type  ReadinessProbe = {
+    auth: Auth,
+    enabled: ServiceBoolean,
+    failureThreshold: ServiceString,
+    initialDelaySeconds: ServiceString,
+    path: ServiceString,
+    periodSeconds: ServiceString,
+    scheme: ServiceString,
+    successThreshold: ServiceString,
+    timeoutSeconds: ServiceString,
+}
+type  StartUpProbe = {
+    auth: Auth,
+    enabled: ServiceBoolean,
+    failureThreshold: ServiceString,
+    path: ServiceString,
+    periodSeconds: ServiceString,
+    scheme: ServiceString,
+    timeoutSeconds: ServiceString,
+}
+
+
+
+type Health = {
+    livenessProbe: LivenessProbe,
+    startupProbe: StartUpProbe,
+    readinessProbe: ReadinessProbe,
+    livenessCommand: livenessCommand,
+}
+
+
+
 const ServiceField = {
     string: (defaultValue: string, overrideValue?: string): ServiceString => {
         return {
@@ -119,6 +222,7 @@ export type WebService = SharedServiceParams & Omit<WorkerService, 'type'> & {
     type: 'web';
     port: ServiceString;
     ingress: Ingress;
+    health: Health;
 }
 const WebService = {
     default: (name: string, porterJson?: PorterJson): WebService => ({
@@ -142,6 +246,60 @@ const WebService = {
         },
         port: ServiceField.string('3000', porterJson?.apps?.[name]?.config?.container?.port),
         canDelete: porterJson?.apps?.[name] == null,
+        health: {
+            startupProbe:{
+                auth:{
+                    enabled: ServiceField.boolean(false, porterJson?.apps?.[name]?.config?.health?.startupProbe?.auth?.enabled),
+                    password: ServiceField.string('', porterJson?.apps?.[name]?.config?.health?.startupProbe?.auth?.password),
+                    username: ServiceField.string('', porterJson?.apps?.[name]?.config?.health?.startupProbe?.auth?.username)
+                },
+                enabled: ServiceField.boolean(false, porterJson?.apps?.[name]?.config?.health?.startupProbe?.enabled),
+                failureThreshold: ServiceField.string('3', porterJson?.apps?.[name]?.config?.health?.startupProbe?.failureThreshold),
+                path: ServiceField.string('/startupz', porterJson?.apps?.[name]?.config?.health?.startupProbe?.path),
+                periodSeconds: ServiceField.string('5', porterJson?.apps?.[name]?.config?.health?.startupProbe?.periodSeconds),
+                scheme: ServiceField.string('HTTP', porterJson?.apps?.[name]?.config?.health?.startupProbe?.scheme),
+                timeoutSeconds: ServiceField.string('1', porterJson?.apps?.[name]?.config?.health?.startupProbe?.timeoutSeconds),
+            },
+            readinessProbe:{
+                auth:{
+                    enabled: ServiceField.boolean(false, porterJson?.apps?.[name]?.config?.health?.readinessProbe?.auth?.enabled),
+                    password: ServiceField.string('', porterJson?.apps?.[name]?.config?.health?.readinessProbe?.auth?.password),
+                    username: ServiceField.string('', porterJson?.apps?.[name]?.config?.health?.readinessProbe?.auth?.username)
+                },
+                enabled: ServiceField.boolean(false, porterJson?.apps?.[name]?.config?.health?.readinessProbe?.enabled),
+                failureThreshold: ServiceField.string('3', porterJson?.apps?.[name]?.config?.health?.readinessProbe?.failureThreshold),
+                initialDelaySeconds: ServiceField.string('0', porterJson?.apps?.[name]?.config?.health?.readinessProbe?.initialDelaySeconds),
+                path: ServiceField.string('/readyz', porterJson?.apps?.[name]?.config?.health?.readinessProbe?.path),
+                periodSeconds: ServiceField.string('5', porterJson?.apps?.[name]?.config?.health?.readinessProbe?.periodSeconds),
+                scheme: ServiceField.string('HTTP', porterJson?.apps?.[name]?.config?.health?.readinessProbe?.scheme),
+                timeoutSeconds: ServiceField.string('1', porterJson?.apps?.[name]?.config?.health?.readinessProbe?.timeoutSeconds),
+                successThreshold: ServiceField.string('1', porterJson?.apps?.[name]?.config?.health?.readinessProbe?.successThreshold),
+            },
+            livenessCommand:{
+                command: ServiceField.string('ls -l', porterJson?.apps?.[name]?.config?.health?.livenessCommand?.command),
+                enabled: ServiceField.boolean(false, porterJson?.apps?.[name]?.config?.health?.livenessCommand?.enabled),
+                failureThreshold: ServiceField.string('3', porterJson?.apps?.[name]?.config?.health?.livenessCommand?.failureThreshold),
+                initialDelaySeconds: ServiceField.string('5', porterJson?.apps?.[name]?.config?.health?.livenessCommand?.initialDelaySeconds),
+                periodSeconds: ServiceField.string('5', porterJson?.apps?.[name]?.config?.health?.livenessCommand?.periodSeconds),
+                timeoutSeconds: ServiceField.string('1', porterJson?.apps?.[name]?.config?.health?.livenessCommand?.timeoutSeconds),
+                successThreshold: ServiceField.string('1', porterJson?.apps?.[name]?.config?.health?.livenessCommand?.successThreshold),
+            },
+            livenessProbe:{
+                auth:{
+                    enabled: ServiceField.boolean(false, porterJson?.apps?.[name]?.config?.health?.livenessProbe?.auth?.enabled),
+                    password: ServiceField.string('', porterJson?.apps?.[name]?.config?.health?.livenessProbe?.auth?.password),
+                    username: ServiceField.string('', porterJson?.apps?.[name]?.config?.health?.livenessProbe?.auth?.username)
+                },
+                failureThreshold: ServiceField.string('3', porterJson?.apps?.[name]?.config?.health?.livenessProbe?.failureThreshold),
+                initialDelaySeconds: ServiceField.string('0', porterJson?.apps?.[name]?.config?.health?.livenessProbe?.initialDelaySeconds),
+                path: ServiceField.string('/livez', porterJson?.apps?.[name]?.config?.health?.livenessProbe?.path),
+                periodSeconds: ServiceField.string('5', porterJson?.apps?.[name]?.config?.health?.livenessProbe?.periodSeconds),
+                scheme: ServiceField.string('HTTP', porterJson?.apps?.[name]?.config?.health?.livenessProbe?.scheme),
+                successThreshold: ServiceField.string('1', porterJson?.apps?.[name]?.config?.health?.livenessProbe?.successThreshold),
+                timeoutSeconds: ServiceField.string('1', porterJson?.apps?.[name]?.config?.health?.livenessProbe?.timeoutSeconds),
+                enabled: ServiceField.boolean(false, porterJson?.apps?.[name]?.config?.health?.livenessProbe?.enabled),
+            },
+        }
     }),
     serialize: (service: WebService) => {
         return {
@@ -171,6 +329,11 @@ const WebService = {
                 hosts: service.ingress.hosts.value ? [service.ingress.hosts.value] : [],
                 custom_domain: service.ingress.hosts.value ? true : false,
                 porter_hosts: service.ingress.porterHosts.value ? [service.ingress.porterHosts.value] : [],
+            },
+            health: {
+                livenessProbe:{
+                    enabled: ServiceField.boolean(service.health?.livenessProbe?.enabled.value),
+                },
             }
         }
     },
@@ -196,6 +359,11 @@ const WebService = {
             },
             port: ServiceField.string(values.container?.port ?? '', porterJson?.apps?.[name]?.config?.container?.port),
             canDelete: porterJson?.apps?.[name] == null,
+            health: {
+                livenessProbe:{
+                    enabled: ServiceField.boolean(values.health?.livenessProbe?.enabled ?? false, porterJson?.apps?.[name]?.config?.health.livenessProbe?.enabled),
+                },
+            }
         }
     }
 }