Răsfoiți Sursa

add additional config to health check (#4354)

d-g-town 2 ani în urmă
părinte
comite
d8ed12680d

+ 7 - 7
dashboard/package-lock.json

@@ -95,7 +95,7 @@
         "@babel/preset-typescript": "^7.15.0",
         "@ianvs/prettier-plugin-sort-imports": "^4.1.1",
         "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
-        "@porter-dev/api-contracts": "^0.2.112",
+        "@porter-dev/api-contracts": "^0.2.113",
         "@testing-library/jest-dom": "^4.2.4",
         "@testing-library/react": "^9.3.2",
         "@testing-library/user-event": "^7.1.2",
@@ -2754,9 +2754,9 @@
       }
     },
     "node_modules/@porter-dev/api-contracts": {
-      "version": "0.2.112",
-      "resolved": "https://registry.npmjs.org/@porter-dev/api-contracts/-/api-contracts-0.2.112.tgz",
-      "integrity": "sha512-O+BN4GNe3YQXzatCFOm3SNyZEg+iYrlrcLv9M2pwmur0bBg1lHszHYAa1idTMivcGVW3ynAO1kMGpjHRdOHgOA==",
+      "version": "0.2.113",
+      "resolved": "https://registry.npmjs.org/@porter-dev/api-contracts/-/api-contracts-0.2.113.tgz",
+      "integrity": "sha512-pk6JMuY/qSVMIcC7lw28PGPHcHT7qCn1xosug8TvpJ3fMNav1seotgBpqPh4CUQ8b1cF5PtAZWvEN+dx4bt/qg==",
       "dev": true,
       "dependencies": {
         "@bufbuild/protobuf": "^1.1.0"
@@ -20056,9 +20056,9 @@
       "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
     },
     "@porter-dev/api-contracts": {
-      "version": "0.2.112",
-      "resolved": "https://registry.npmjs.org/@porter-dev/api-contracts/-/api-contracts-0.2.112.tgz",
-      "integrity": "sha512-O+BN4GNe3YQXzatCFOm3SNyZEg+iYrlrcLv9M2pwmur0bBg1lHszHYAa1idTMivcGVW3ynAO1kMGpjHRdOHgOA==",
+      "version": "0.2.113",
+      "resolved": "https://registry.npmjs.org/@porter-dev/api-contracts/-/api-contracts-0.2.113.tgz",
+      "integrity": "sha512-pk6JMuY/qSVMIcC7lw28PGPHcHT7qCn1xosug8TvpJ3fMNav1seotgBpqPh4CUQ8b1cF5PtAZWvEN+dx4bt/qg==",
       "dev": true,
       "requires": {
         "@bufbuild/protobuf": "^1.1.0"

+ 1 - 1
dashboard/package.json

@@ -102,7 +102,7 @@
     "@babel/preset-typescript": "^7.15.0",
     "@ianvs/prettier-plugin-sort-imports": "^4.1.1",
     "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
-    "@porter-dev/api-contracts": "^0.2.112",
+    "@porter-dev/api-contracts": "^0.2.113",
     "@testing-library/jest-dom": "^4.2.4",
     "@testing-library/react": "^9.3.2",
     "@testing-library/user-event": "^7.1.2",

+ 4 - 0
dashboard/src/lib/porter-apps/services.ts

@@ -243,11 +243,15 @@ export function defaultSerialized({
   const defaultWebHealthCheck: SerializedHealthcheck = {
     enabled: false,
     httpPath: "/healthz",
+    timeoutSeconds: 1,
+    initialDelaySeconds: 15,
   };
 
   const defaultWorkerHealthCheck: SerializedHealthcheck = {
     enabled: false,
     command: "./healthz.sh",
+    timeoutSeconds: 1,
+    initialDelaySeconds: 15,
   };
 
   return match(type)

+ 18 - 1
dashboard/src/lib/porter-apps/values.ts

@@ -34,7 +34,7 @@ const getNumericValue = (
   defaultValue: number,
   overrideValue?: number,
   validAsZero = false
-) => {
+): number => {
   if (!overrideValue) {
     return defaultValue;
   }
@@ -161,12 +161,16 @@ export const healthcheckValidator = z.object({
   enabled: serviceBooleanValidator,
   httpPath: serviceStringValidator.optional(),
   command: serviceStringValidator.optional(),
+  timeoutSeconds: serviceNumberValidator.optional(),
+  initialDelaySeconds: serviceNumberValidator.optional(),
 });
 export type ClientHealthCheck = z.infer<typeof healthcheckValidator>;
 export type SerializedHealthcheck = {
   enabled: boolean;
   httpPath?: string;
   command?: string;
+  timeoutSeconds?: number;
+  initialDelaySeconds?: number;
 };
 
 export function serializeHealth({
@@ -179,6 +183,8 @@ export function serializeHealth({
       enabled: health.enabled.value,
       httpPath: health.httpPath?.value,
       command: health.command?.value,
+      timeoutSeconds: health.timeoutSeconds?.value,
+      initialDelaySeconds: health.initialDelaySeconds?.value,
     }
   );
 }
@@ -200,12 +206,23 @@ export function deserializeHealthCheck({
         command: health.command
           ? ServiceField.string(health.command, override?.command)
           : ServiceField.string("", undefined),
+        timeoutSeconds: health.timeoutSeconds
+          ? ServiceField.number(health.timeoutSeconds, override?.timeoutSeconds)
+          : ServiceField.number(1, undefined),
+        initialDelaySeconds: health.initialDelaySeconds
+          ? ServiceField.number(
+              health.initialDelaySeconds,
+              override?.initialDelaySeconds
+            )
+          : ServiceField.number(15, undefined),
       }
     : setDefaults
     ? {
         enabled: ServiceField.boolean(false, undefined),
         httpPath: ServiceField.string("", undefined),
         command: ServiceField.string("", undefined),
+        timeoutSeconds: ServiceField.number(1, undefined),
+        initialDelaySeconds: ServiceField.number(15, undefined),
       }
     : undefined;
 }

+ 1 - 1
dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Advanced.tsx

@@ -16,7 +16,7 @@ const Advanced: React.FC<AdvancedProps> = ({ index }) => {
 
   return (
     <>
-      <Text>Termination grace period seconds</Text>
+      <Text>Termination grace period (seconds)</Text>
       <Spacer y={0.25} />
       <Container style={{ width: "400px" }}>
         <Text color="helper">

+ 50 - 28
dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Health.tsx

@@ -60,34 +60,56 @@ const Health: React.FC<HealthProps> = ({ index }) => {
           </Checkbox>
         )}
       />
-      {healthCheckEnabled.value &&
-        (serviceType === "web" ? (
-          <>
-            <Spacer y={0.5} />
-            <Text>Health check endpoint</Text>
-            <Spacer y={0.5} />
-            <ControlledInput
-              type="text"
-              placeholder="ex: /healthz"
-              {...register(
-                `app.services.${index}.config.healthCheck.httpPath.value`
-              )}
-            />
-          </>
-        ) : (
-          <>
-            <Spacer y={0.5} />
-            <Text>Health check command</Text>
-            <Spacer y={0.5} />
-            <ControlledInput
-              type="text"
-              placeholder="ex: ./healthz.sh"
-              {...register(
-                `app.services.${index}.config.healthCheck.command.value`
-              )}
-            />
-          </>
-        ))}
+      {healthCheckEnabled.value && (
+        <>
+          <Spacer y={0.75} />
+          {serviceType === "web" ? (
+            <>
+              <Text color="helper">Endpoint</Text>
+              <Spacer y={0.25} />
+              <ControlledInput
+                type="text"
+                placeholder="ex: /healthz"
+                {...register(
+                  `app.services.${index}.config.healthCheck.httpPath.value`
+                )}
+              />
+            </>
+          ) : (
+            <>
+              <Text color="helper">Command</Text>
+              <Spacer y={0.25} />
+              <ControlledInput
+                type="text"
+                placeholder="ex: ./healthz.sh"
+                {...register(
+                  `app.services.${index}.config.healthCheck.command.value`
+                )}
+              />
+            </>
+          )}
+          <Spacer y={0.5} />
+          <Text color="helper">Timeout (seconds)</Text>
+          <Spacer y={0.25} />
+          <ControlledInput
+            type="text"
+            placeholder="ex: 1"
+            {...register(
+              `app.services.${index}.config.healthCheck.timeoutSeconds.value`
+            )}
+          />
+          <Spacer y={0.5} />
+          <Text color="helper">Initial delay (seconds)</Text>
+          <Spacer y={0.25} />
+          <ControlledInput
+            type="text"
+            placeholder="ex: 30"
+            {...register(
+              `app.services.${index}.config.healthCheck.initialDelaySeconds.value`
+            )}
+          />
+        </>
+      )}
     </>
   );
 };

+ 1 - 1
go.mod

@@ -83,7 +83,7 @@ require (
 	github.com/matryer/is v1.4.0
 	github.com/nats-io/nats.go v1.24.0
 	github.com/open-policy-agent/opa v0.44.0
-	github.com/porter-dev/api-contracts v0.2.112
+	github.com/porter-dev/api-contracts v0.2.113
 	github.com/riandyrn/otelchi v0.5.1
 	github.com/santhosh-tekuri/jsonschema/v5 v5.0.1
 	github.com/stefanmcshane/helm v0.0.0-20221213002717-88a4a2c6e77d

+ 2 - 2
go.sum

@@ -1523,8 +1523,8 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw=
-github.com/porter-dev/api-contracts v0.2.112 h1:ZywNLFVopYsZvV5WkjFIXRxoPI64wvJYsy4RMLCTgAY=
-github.com/porter-dev/api-contracts v0.2.112/go.mod h1:fX6JmP5QuzxDLvqP3evFOTXjI4dHxsG0+VKNTjImZU8=
+github.com/porter-dev/api-contracts v0.2.113 h1:sv1huO9MpykJaWhV2D5zTD2LouMbRSBV5ATt/5Ukrbo=
+github.com/porter-dev/api-contracts v0.2.113/go.mod h1:fX6JmP5QuzxDLvqP3evFOTXjI4dHxsG0+VKNTjImZU8=
 github.com/porter-dev/switchboard v0.0.3 h1:dBuYkiVLa5Ce7059d6qTe9a1C2XEORFEanhbtV92R+M=
 github.com/porter-dev/switchboard v0.0.3/go.mod h1:xSPzqSFMQ6OSbp42fhCi4AbGbQbsm6nRvOkrblFeXU4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=

+ 7 - 3
internal/porter_app/test/parse_test.go

@@ -77,9 +77,11 @@ var result_nobuild = &porterv1.PorterApp{
 						},
 					},
 					HealthCheck: &porterv1.HealthCheck{
-						Enabled:  true,
-						HttpPath: "/healthz",
-						Command:  "",
+						Enabled:             true,
+						HttpPath:            "/healthz",
+						Command:             "",
+						InitialDelaySeconds: &initialDelaySeconds10,
+						TimeoutSeconds:      5,
 					},
 				},
 			},
@@ -230,6 +232,8 @@ var v1_result_nobuild_no_image = &porterv1.PorterApp{
 	},
 }
 
+var initialDelaySeconds10 = int32(10)
+
 func diffProtoWithFailTest(t *testing.T, is *is.I, want, got *porterv1.PorterApp) {
 	t.Helper()
 

+ 2 - 1
internal/porter_app/testdata/v2_input_no_build_no_env.yaml

@@ -26,7 +26,8 @@ services:
     healthCheck:
       enabled: true
       httpPath: /healthz
-      command: ""
+      timeoutSeconds: 5
+      initialDelaySeconds: 10
   - name: example-wkr
     type: worker
     run: echo 'work'

+ 2 - 0
internal/porter_app/testdata/v2_input_nobuild.yaml

@@ -23,6 +23,8 @@ services:
     healthCheck:
       enabled: true
       httpPath: /healthz
+      timeoutSeconds: 5
+      initialDelaySeconds: 10
   - name: example-wkr
     type: worker
     run: echo 'work'

+ 25 - 15
internal/porter_app/v2/yaml.go

@@ -213,9 +213,11 @@ type Domains struct {
 
 // HealthCheck contains the health check settings
 type HealthCheck struct {
-	Enabled  bool   `yaml:"enabled"`
-	HttpPath string `yaml:"httpPath"`
-	Command  string `yaml:"command"`
+	Enabled             bool   `yaml:"enabled"`
+	HttpPath            string `yaml:"httpPath,omitempty"`
+	Command             string `yaml:"command,omitempty"`
+	TimeoutSeconds      int    `yaml:"timeoutSeconds,omitempty"`
+	InitialDelaySeconds *int32 `yaml:"initialDelaySeconds,omitempty"`
 }
 
 // ProtoFromApp converts a PorterApp type to a base PorterApp proto type and returns env variables
@@ -416,9 +418,11 @@ func serviceProtoFromConfig(service Service, serviceType porterv1.ServiceType) (
 		var healthCheck *porterv1.HealthCheck
 		if service.HealthCheck != nil {
 			healthCheck = &porterv1.HealthCheck{
-				Enabled:  service.HealthCheck.Enabled,
-				HttpPath: service.HealthCheck.HttpPath,
-				Command:  service.HealthCheck.Command,
+				Enabled:             service.HealthCheck.Enabled,
+				HttpPath:            service.HealthCheck.HttpPath,
+				Command:             service.HealthCheck.Command,
+				TimeoutSeconds:      int32(service.HealthCheck.TimeoutSeconds),
+				InitialDelaySeconds: service.HealthCheck.InitialDelaySeconds,
 			}
 		}
 		webConfig.HealthCheck = healthCheck
@@ -462,9 +466,11 @@ func serviceProtoFromConfig(service Service, serviceType porterv1.ServiceType) (
 		var healthCheck *porterv1.HealthCheck
 		if service.HealthCheck != nil {
 			healthCheck = &porterv1.HealthCheck{
-				Enabled:  service.HealthCheck.Enabled,
-				HttpPath: service.HealthCheck.HttpPath,
-				Command:  service.HealthCheck.Command,
+				Enabled:             service.HealthCheck.Enabled,
+				HttpPath:            service.HealthCheck.HttpPath,
+				Command:             service.HealthCheck.Command,
+				TimeoutSeconds:      int32(service.HealthCheck.TimeoutSeconds),
+				InitialDelaySeconds: service.HealthCheck.InitialDelaySeconds,
 			}
 		}
 		workerConfig.HealthCheck = healthCheck
@@ -602,9 +608,11 @@ func appServiceFromProto(service *porterv1.Service) (Service, error) {
 		var healthCheck *HealthCheck
 		if webConfig.HealthCheck != nil {
 			healthCheck = &HealthCheck{
-				Enabled:  webConfig.HealthCheck.Enabled,
-				HttpPath: webConfig.HealthCheck.HttpPath,
-				Command:  webConfig.HealthCheck.Command,
+				Enabled:             webConfig.HealthCheck.Enabled,
+				HttpPath:            webConfig.HealthCheck.HttpPath,
+				Command:             webConfig.HealthCheck.Command,
+				TimeoutSeconds:      int(webConfig.HealthCheck.TimeoutSeconds),
+				InitialDelaySeconds: webConfig.HealthCheck.InitialDelaySeconds,
 			}
 		}
 		appService.HealthCheck = healthCheck
@@ -645,9 +653,11 @@ func appServiceFromProto(service *porterv1.Service) (Service, error) {
 		var healthCheck *HealthCheck
 		if workerConfig.HealthCheck != nil {
 			healthCheck = &HealthCheck{
-				Enabled:  workerConfig.HealthCheck.Enabled,
-				HttpPath: workerConfig.HealthCheck.HttpPath,
-				Command:  workerConfig.HealthCheck.Command,
+				Enabled:             workerConfig.HealthCheck.Enabled,
+				HttpPath:            workerConfig.HealthCheck.HttpPath,
+				Command:             workerConfig.HealthCheck.Command,
+				TimeoutSeconds:      int(workerConfig.HealthCheck.TimeoutSeconds),
+				InitialDelaySeconds: workerConfig.HealthCheck.InitialDelaySeconds,
 			}
 		}
 		appService.HealthCheck = healthCheck