From d8ed12680da53802cbcb2f5ccf2a2e013035abaf Mon Sep 17 00:00:00 2001 From: d-g-town <66391417+d-g-town@users.noreply.github.com> Date: Mon, 4 Mar 2024 10:33:40 -0500 Subject: [PATCH] add additional config to health check (#4354) --- dashboard/package-lock.json | 14 ++-- dashboard/package.json | 2 +- dashboard/src/lib/porter-apps/services.ts | 4 + dashboard/src/lib/porter-apps/values.ts | 19 ++++- .../services-settings/tabs/Advanced.tsx | 2 +- .../services-settings/tabs/Health.tsx | 78 ++++++++++++------- go.mod | 2 +- go.sum | 4 +- internal/porter_app/test/parse_test.go | 10 ++- .../testdata/v2_input_no_build_no_env.yaml | 3 +- .../porter_app/testdata/v2_input_nobuild.yaml | 2 + internal/porter_app/v2/yaml.go | 40 ++++++---- 12 files changed, 120 insertions(+), 60 deletions(-) diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index 9fcbe8e13c..abb75fda99 100644 --- a/dashboard/package-lock.json +++ b/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" diff --git a/dashboard/package.json b/dashboard/package.json index 2bcbd77264..4b6da867c6 100644 --- a/dashboard/package.json +++ b/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", diff --git a/dashboard/src/lib/porter-apps/services.ts b/dashboard/src/lib/porter-apps/services.ts index b3839fcb2a..ae81cd4c4b 100644 --- a/dashboard/src/lib/porter-apps/services.ts +++ b/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) diff --git a/dashboard/src/lib/porter-apps/values.ts b/dashboard/src/lib/porter-apps/values.ts index 60b48bb09b..ba46649d06 100644 --- a/dashboard/src/lib/porter-apps/values.ts +++ b/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; 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; } diff --git a/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Advanced.tsx b/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Advanced.tsx index c6c3cfbb7b..f215b969cf 100644 --- a/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Advanced.tsx +++ b/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Advanced.tsx @@ -16,7 +16,7 @@ const Advanced: React.FC = ({ index }) => { return ( <> - Termination grace period seconds + Termination grace period (seconds) diff --git a/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Health.tsx b/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Health.tsx index fc1e3d4eb5..43312e5532 100644 --- a/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Health.tsx +++ b/dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Health.tsx @@ -60,34 +60,56 @@ const Health: React.FC = ({ index }) => { )} /> - {healthCheckEnabled.value && - (serviceType === "web" ? ( - <> - - Health check endpoint - - - - ) : ( - <> - - Health check command - - - - ))} + {healthCheckEnabled.value && ( + <> + + {serviceType === "web" ? ( + <> + Endpoint + + + + ) : ( + <> + Command + + + + )} + + Timeout (seconds) + + + + Initial delay (seconds) + + + + )} ); }; diff --git a/go.mod b/go.mod index 767bf296d0..f237f89f62 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index cc9e3fbf87..8ad6491a21 100644 --- a/go.sum +++ b/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= diff --git a/internal/porter_app/test/parse_test.go b/internal/porter_app/test/parse_test.go index 0e7118a0ed..489c67fa32 100644 --- a/internal/porter_app/test/parse_test.go +++ b/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() diff --git a/internal/porter_app/testdata/v2_input_no_build_no_env.yaml b/internal/porter_app/testdata/v2_input_no_build_no_env.yaml index 6ebd3c8a35..b1ba8efcad 100644 --- a/internal/porter_app/testdata/v2_input_no_build_no_env.yaml +++ b/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' diff --git a/internal/porter_app/testdata/v2_input_nobuild.yaml b/internal/porter_app/testdata/v2_input_nobuild.yaml index 0cef0daa24..d10bcdb7f1 100644 --- a/internal/porter_app/testdata/v2_input_nobuild.yaml +++ b/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' diff --git a/internal/porter_app/v2/yaml.go b/internal/porter_app/v2/yaml.go index 8ab1aadc89..89e28082e5 100644 --- a/internal/porter_app/v2/yaml.go +++ b/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