From 8f39f1f456da0eacab3e9c5fb151b2ebfbccda48 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 21 Dec 2023 09:45:22 -0500 Subject: [PATCH 01/14] update validation --- .../LoadBalancerCreate/ServiceTargetForm.tsx | 5 ++--- .../ServiceTargets/ServiceTargetDrawer.tsx | 1 - packages/validation/src/loadbalancers.schema.ts | 14 +++++++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx b/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx index 72806252b4c..7611d7bf6b0 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx @@ -319,21 +319,20 @@ export const ServiceTargetForm = (props: Props) => { {formik.values.healthcheck.protocol === 'http' && ( <> diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx index cd02f396ccd..3189455dd84 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx @@ -378,7 +378,6 @@ export const ServiceTargetDrawer = (props: Props) => { label="Health Check Host" name="healthcheck.host" onChange={formik.handleChange} - optional value={formik.values.healthcheck.host} /> diff --git a/packages/validation/src/loadbalancers.schema.ts b/packages/validation/src/loadbalancers.schema.ts index 1153342ccd0..ad02d6aa5d7 100644 --- a/packages/validation/src/loadbalancers.schema.ts +++ b/packages/validation/src/loadbalancers.schema.ts @@ -50,8 +50,12 @@ export const EndpointSchema = object({ const HealthCheckSchema = object({ protocol: string().oneOf(['http', 'tcp']), - interval: number().typeError('Interval must be a number.').min(1, 'Interval must be greater than zero.'), - timeout: number().typeError('Timeout must be a number.').min(1, 'Timeout must be greater than zero.'), + interval: number() + .typeError('Interval must be a number.') + .min(1, 'Interval must be greater than zero.'), + timeout: number() + .typeError('Timeout must be a number.') + .min(1, 'Timeout must be greater than zero.'), unhealthy_threshold: number() .typeError('Unhealthy Threshold must be a number.') .min(1, 'Unhealthy Threshold must be greater than zero.'), @@ -59,7 +63,11 @@ const HealthCheckSchema = object({ .typeError('Healthy Threshold must be a number.') .min(1, 'Healthy Threshold must be greater than zero.'), path: string().nullable(), - host: string().nullable(), + host: string().when('protocol', { + is: 'tcp', + then: (o) => o.nullable(), + otherwise: (o) => o.required(), + }), }); export const CreateServiceTargetSchema = object({ From 9e974c3a7f9668a6dde311e3cace95e7e09630b3 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 21 Dec 2023 09:57:09 -0500 Subject: [PATCH 02/14] improve validation --- .../LoadBalancerCreate/ServiceTargetForm.tsx | 17 ++++++++++++++--- .../ServiceTargets/ServiceTargetDrawer.tsx | 17 ++++++++++++++--- packages/validation/src/loadbalancers.schema.ts | 4 ++-- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx b/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx index 7611d7bf6b0..669b433d5a9 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx @@ -128,10 +128,11 @@ export const ServiceTargetForm = (props: Props) => { return (
@@ -319,19 +320,29 @@ export const ServiceTargetForm = (props: Props) => { {formik.values.healthcheck.protocol === 'http' && ( <> diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx index 3189455dd84..346bb2904fd 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx @@ -168,9 +168,10 @@ export const ServiceTargetDrawer = (props: Props) => { /> )} @@ -360,23 +361,33 @@ export const ServiceTargetDrawer = (props: Props) => { {formik.values.healthcheck.protocol === 'http' && ( <> diff --git a/packages/validation/src/loadbalancers.schema.ts b/packages/validation/src/loadbalancers.schema.ts index ad02d6aa5d7..eda9069b1fa 100644 --- a/packages/validation/src/loadbalancers.schema.ts +++ b/packages/validation/src/loadbalancers.schema.ts @@ -65,8 +65,8 @@ const HealthCheckSchema = object({ path: string().nullable(), host: string().when('protocol', { is: 'tcp', - then: (o) => o.nullable(), - otherwise: (o) => o.required(), + then: (o) => o.notRequired(), + otherwise: (o) => o.required('Health Check host is required.'), }), }); From 9408ed8f02753c6632ab6d24fa8139904518e158 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 21 Dec 2023 09:59:51 -0500 Subject: [PATCH 03/14] fix other bug --- .../LoadBalancers/LoadBalancerDetail/Routes/EditRouteDrawer.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/Routes/EditRouteDrawer.tsx b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/Routes/EditRouteDrawer.tsx index e66b51d24e3..8ad993156c2 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/Routes/EditRouteDrawer.tsx +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/Routes/EditRouteDrawer.tsx @@ -40,6 +40,7 @@ export const EditRouteDrawer = (props: Props) => { initialValues: { label: route?.label, protocol: route?.protocol, + rules: route?.rules, // We shouldn't have to do this, but the API clears out the rules if this isnt passed }, async onSubmit(values) { try { From 829c4e8f7a63d301b25bd998a45f5837019b13b6 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 21 Dec 2023 10:21:00 -0500 Subject: [PATCH 04/14] Added changeset: AGLB route rules being cleared when updating a route --- packages/manager/.changeset/pr-10016-fixed-1703172060704.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/manager/.changeset/pr-10016-fixed-1703172060704.md diff --git a/packages/manager/.changeset/pr-10016-fixed-1703172060704.md b/packages/manager/.changeset/pr-10016-fixed-1703172060704.md new file mode 100644 index 00000000000..62ec8c3de7f --- /dev/null +++ b/packages/manager/.changeset/pr-10016-fixed-1703172060704.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +AGLB route rules being cleared when updating a route ([#10016](https://github.com/linode/manager/pull/10016)) From 57f059901c19da44751841b93eb9bdd5535b4f48 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 21 Dec 2023 10:21:48 -0500 Subject: [PATCH 05/14] Added changeset: AGLB Service Target validation --- packages/manager/.changeset/pr-10016-fixed-1703172108441.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/manager/.changeset/pr-10016-fixed-1703172108441.md diff --git a/packages/manager/.changeset/pr-10016-fixed-1703172108441.md b/packages/manager/.changeset/pr-10016-fixed-1703172108441.md new file mode 100644 index 00000000000..cd77df12b4c --- /dev/null +++ b/packages/manager/.changeset/pr-10016-fixed-1703172108441.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +AGLB Service Target validation ([#10016](https://github.com/linode/manager/pull/10016)) From bbb834b194f3a9b9a247a355fba4ae43c288a0bc Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 21 Dec 2023 10:27:26 -0500 Subject: [PATCH 06/14] add placheolder test --- .../LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx b/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx index 669b433d5a9..c6f501391cf 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx @@ -331,6 +331,7 @@ export const ServiceTargetForm = (props: Props) => { onBlur={formik.handleBlur} onChange={formik.handleChange} optional + placeholder="/" value={formik.values.healthcheck.path} /> { name="healthcheck.host" onBlur={formik.handleBlur} onChange={formik.handleChange} + placeholder="example.org" value={formik.values.healthcheck.host} /> From f84c84ec5e99d33f02b2ced52c25551f1804999e Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 21 Dec 2023 10:42:59 -0500 Subject: [PATCH 07/14] disable save in edit mode when there is no changes --- .../LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx index 346bb2904fd..b4a08c35a93 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx @@ -374,6 +374,7 @@ export const ServiceTargetDrawer = (props: Props) => { onBlur={formik.handleBlur} onChange={formik.handleChange} optional + placeholder="/" value={formik.values.healthcheck.path} /> { name="healthcheck.host" onBlur={formik.handleBlur} onChange={formik.handleChange} + placeholder="example.org" value={formik.values.healthcheck.host} /> @@ -397,6 +399,7 @@ export const ServiceTargetDrawer = (props: Props) => { )} Date: Thu, 21 Dec 2023 13:17:42 -0500 Subject: [PATCH 08/14] fix e2e test by filling out required form --- .../core/loadBalancers/load-balancer-service-targets.spec.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/manager/cypress/e2e/core/loadBalancers/load-balancer-service-targets.spec.ts b/packages/manager/cypress/e2e/core/loadBalancers/load-balancer-service-targets.spec.ts index 75a33020556..b00d5b14df4 100644 --- a/packages/manager/cypress/e2e/core/loadBalancers/load-balancer-service-targets.spec.ts +++ b/packages/manager/cypress/e2e/core/loadBalancers/load-balancer-service-targets.spec.ts @@ -211,6 +211,11 @@ describe('Akamai Global Load Balancer service targets', () => { .should('be.visible') .click(); + cy.findByLabelText('Health Check Host') + .scrollIntoView() + .should('be.visible') + .type('example.com'); + cy.get('[data-qa-healthcheck-options]') .scrollIntoView() .should('be.visible'); From 5322bea4efaabaabc6145dc7360fc4ad7c5ed326 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Tue, 2 Jan 2024 09:10:03 -0500 Subject: [PATCH 09/14] remove ability to toggle service target health checks --- .../load-balancer-service-targets.spec.ts | 39 --- .../LoadBalancerCreate/ServiceTargetForm.tsx | 243 ++++++++--------- .../ServiceTargets/ServiceTargetDrawer.tsx | 249 ++++++++---------- .../ServiceTargets/constants.tsx | 2 +- 4 files changed, 219 insertions(+), 314 deletions(-) diff --git a/packages/manager/cypress/e2e/core/loadBalancers/load-balancer-service-targets.spec.ts b/packages/manager/cypress/e2e/core/loadBalancers/load-balancer-service-targets.spec.ts index b00d5b14df4..ea8a271d5d9 100644 --- a/packages/manager/cypress/e2e/core/loadBalancers/load-balancer-service-targets.spec.ts +++ b/packages/manager/cypress/e2e/core/loadBalancers/load-balancer-service-targets.spec.ts @@ -200,26 +200,11 @@ describe('Akamai Global Load Balancer service targets', () => { .should('be.visible') .click(); - // Confirm that health check options are hidden when health check is disabled. - cy.findByText('Use Health Checks').should('be.visible').click(); - - cy.get('[data-qa-healthcheck-options]').should('not.exist'); - - // Re-enable health check, fill out form. - cy.findByText('Use Health Checks') - .scrollIntoView() - .should('be.visible') - .click(); - cy.findByLabelText('Health Check Host') .scrollIntoView() .should('be.visible') .type('example.com'); - cy.get('[data-qa-healthcheck-options]') - .scrollIntoView() - .should('be.visible'); - ui.button .findByTitle('Create Service Target') .scrollIntoView() @@ -387,30 +372,6 @@ describe('Akamai Global Load Balancer service targets', () => { mockServiceTarget.healthcheck.unhealthy_threshold ); - // Confirm that health check options are hidden when health check is disabled. - cy.findByText('Use Health Checks').should('be.visible').click(); - - cy.get('[data-qa-healthcheck-options]').should('not.exist'); - - // Re-enable health check, fill out form. - cy.findByText('Use Health Checks') - .scrollIntoView() - .should('be.visible') - .click(); - - cy.get('[data-qa-healthcheck-options]') - .scrollIntoView() - .should('be.visible'); - - // Confirm that health check options are restored to defaults after toggle. - cy.findByLabelText('Interval').should('have.value', 10); - - cy.findByLabelText('Timeout').should('have.value', 5000); - - cy.findByLabelText('Healthy Threshold').should('have.value', 5); - - cy.findByLabelText('Unhealthy Threshold').should('have.value', 5); - //Confirm that health check path and host match service target data. cy.findByLabelText('Health Check Path', { exact: false }).should( 'have.value', diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx b/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx index c6f501391cf..62f9946004c 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx @@ -7,7 +7,6 @@ import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { SelectedIcon } from 'src/components/Autocomplete/Autocomplete.styles'; import { BetaChip } from 'src/components/BetaChip/BetaChip'; -import { Box } from 'src/components/Box'; import { Divider } from 'src/components/Divider'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { FormHelperText } from 'src/components/FormHelperText'; @@ -18,7 +17,6 @@ import { Radio } from 'src/components/Radio/Radio'; import { RadioGroup } from 'src/components/RadioGroup'; import { Stack } from 'src/components/Stack'; import { TextField } from 'src/components/TextField'; -import { Toggle } from 'src/components/Toggle/Toggle'; import { TooltipIcon } from 'src/components/TooltipIcon'; import { Typography } from 'src/components/Typography'; @@ -215,142 +213,117 @@ export const ServiceTargetForm = (props: Props) => { text={SERVICE_TARGET_COPY.Tooltips.Healthcheck.Description} /> - - formik.setFieldValue('healthcheck.interval', checked ? 10 : 0) + + formik.setFieldValue('healthcheck.protocol', value) + } + sx={{ marginBottom: '0px !important' }} + value={formik.values.healthcheck.protocol} + > + + Protocol + + + } label="HTTP" value="http" /> + } label="TCP" value="tcp" /> + {formik.errors.healthcheck?.protocol} + + + seconds + ), + }} + errorText={formik.errors.healthcheck?.interval} + label="Interval" + labelTooltipText={SERVICE_TARGET_COPY.Tooltips.Healthcheck.Interval} + name="healthcheck.interval" + onChange={formik.handleChange} + type="number" + value={formik.values.healthcheck.interval} + /> + checks + ), + }} + errorText={formik.errors.healthcheck?.healthy_threshold} + label="Healthy Threshold" + labelTooltipText={SERVICE_TARGET_COPY.Tooltips.Healthcheck.Healthy} + name="healthcheck.healthy_threshold" + onChange={formik.handleChange} + type="number" + value={formik.values.healthcheck.healthy_threshold} + /> + + + seconds + ), + }} + errorText={formik.errors.healthcheck?.timeout} + label="Timeout" + labelTooltipText={SERVICE_TARGET_COPY.Tooltips.Healthcheck.Timeout} + name="healthcheck.timeout" + onChange={formik.handleChange} + type="number" + value={formik.values.healthcheck.timeout} + /> + checks + ), + }} + errorText={formik.errors.healthcheck?.unhealthy_threshold} + label="Unhealthy Threshold" + labelTooltipText={SERVICE_TARGET_COPY.Tooltips.Healthcheck.Unhealthy} + name="healthcheck.unhealthy_threshold" + onChange={formik.handleChange} + type="number" + value={formik.values.healthcheck.unhealthy_threshold} + /> + + {formik.values.healthcheck.protocol === 'http' && ( + <> + - } - label="Use Health Checks" - /> - {formik.values.healthcheck.interval !== 0 && ( - - - formik.setFieldValue('healthcheck.protocol', value) + - - Protocol - - - } label="HTTP" value="http" /> - } label="TCP" value="tcp" /> - - {formik.errors.healthcheck?.protocol} - - - - seconds - ), - }} - labelTooltipText={ - SERVICE_TARGET_COPY.Tooltips.Healthcheck.Interval - } - errorText={formik.errors.healthcheck?.interval} - label="Interval" - name="healthcheck.interval" - onChange={formik.handleChange} - type="number" - value={formik.values.healthcheck.interval} - /> - checks - ), - }} - labelTooltipText={ - SERVICE_TARGET_COPY.Tooltips.Healthcheck.Healthy - } - errorText={formik.errors.healthcheck?.healthy_threshold} - label="Healthy Threshold" - name="healthcheck.healthy_threshold" - onChange={formik.handleChange} - type="number" - value={formik.values.healthcheck.healthy_threshold} - /> - - - seconds - ), - }} - labelTooltipText={ - SERVICE_TARGET_COPY.Tooltips.Healthcheck.Timeout - } - errorText={formik.errors.healthcheck?.timeout} - label="Timeout" - name="healthcheck.timeout" - onChange={formik.handleChange} - type="number" - value={formik.values.healthcheck.timeout} - /> - checks - ), - }} - labelTooltipText={ - SERVICE_TARGET_COPY.Tooltips.Healthcheck.Unhealthy - } - errorText={formik.errors.healthcheck?.unhealthy_threshold} - label="Unhealthy Threshold" - name="healthcheck.unhealthy_threshold" - onChange={formik.handleChange} - type="number" - value={formik.values.healthcheck.unhealthy_threshold} - /> - - {formik.values.healthcheck.protocol === 'http' && ( - <> - - - - )} - + label="Health Check Host" + labelTooltipText={SERVICE_TARGET_COPY.Tooltips.Healthcheck.Host} + name="healthcheck.host" + onBlur={formik.handleBlur} + onChange={formik.handleChange} + placeholder="example.org" + value={formik.values.healthcheck.host} + /> + )} { text={SERVICE_TARGET_COPY.Tooltips.Healthcheck.Description} /> - - formik.setFieldValue('healthcheck.interval', checked ? 10 : 0) + + formik.setFieldValue('healthcheck.protocol', value) + } + sx={{ marginBottom: '0px !important' }} + value={formik.values.healthcheck.protocol} + > + + Protocol + + + } label="HTTP" value="http" /> + } label="TCP" value="tcp" /> + {formik.errors.healthcheck?.protocol} + + + seconds + ), + }} + errorText={formik.errors.healthcheck?.interval} + label="Interval" + labelTooltipText={SERVICE_TARGET_COPY.Tooltips.Healthcheck.Interval} + name="healthcheck.interval" + onChange={formik.handleChange} + type="number" + value={formik.values.healthcheck.interval} + /> + checks + ), + }} + errorText={formik.errors.healthcheck?.healthy_threshold} + label="Healthy Threshold" + labelTooltipText={SERVICE_TARGET_COPY.Tooltips.Healthcheck.Healthy} + name="healthcheck.healthy_threshold" + onChange={formik.handleChange} + type="number" + value={formik.values.healthcheck.healthy_threshold} + /> + + + seconds + ), + }} + errorText={formik.errors.healthcheck?.timeout} + label="Timeout" + labelTooltipText={SERVICE_TARGET_COPY.Tooltips.Healthcheck.Timeout} + name="healthcheck.timeout" + onChange={formik.handleChange} + type="number" + value={formik.values.healthcheck.timeout} + /> + checks + ), + }} + labelTooltipText={ + SERVICE_TARGET_COPY.Tooltips.Healthcheck.Unhealthy + } + errorText={formik.errors.healthcheck?.unhealthy_threshold} + label="Unhealthy Threshold" + name="healthcheck.unhealthy_threshold" + onChange={formik.handleChange} + type="number" + value={formik.values.healthcheck.unhealthy_threshold} + /> + + {formik.values.healthcheck.protocol === 'http' && ( + <> + - } - label="Use Health Checks" - /> - {formik.values.healthcheck.interval !== 0 && ( - - - formik.setFieldValue('healthcheck.protocol', value) + - - Protocol - - - } label="HTTP" value="http" /> - } label="TCP" value="tcp" /> - - {formik.errors.healthcheck?.protocol} - - - - seconds - ), - }} - labelTooltipText={ - SERVICE_TARGET_COPY.Tooltips.Healthcheck.Interval - } - errorText={formik.errors.healthcheck?.interval} - label="Interval" - name="healthcheck.interval" - onChange={formik.handleChange} - type="number" - value={formik.values.healthcheck.interval} - /> - checks - ), - }} - labelTooltipText={ - SERVICE_TARGET_COPY.Tooltips.Healthcheck.Healthy - } - errorText={formik.errors.healthcheck?.healthy_threshold} - label="Healthy Threshold" - name="healthcheck.healthy_threshold" - onChange={formik.handleChange} - type="number" - value={formik.values.healthcheck.healthy_threshold} - /> - - - seconds - ), - }} - labelTooltipText={ - SERVICE_TARGET_COPY.Tooltips.Healthcheck.Timeout - } - errorText={formik.errors.healthcheck?.timeout} - label="Timeout" - name="healthcheck.timeout" - onChange={formik.handleChange} - type="number" - value={formik.values.healthcheck.timeout} - /> - checks - ), - }} - labelTooltipText={ - SERVICE_TARGET_COPY.Tooltips.Healthcheck.Unhealthy - } - errorText={formik.errors.healthcheck?.unhealthy_threshold} - label="Unhealthy Threshold" - name="healthcheck.unhealthy_threshold" - onChange={formik.handleChange} - type="number" - value={formik.values.healthcheck.unhealthy_threshold} - /> - - {formik.values.healthcheck.protocol === 'http' && ( - <> - - - - )} - + label="Health Check Host" + labelTooltipText={SERVICE_TARGET_COPY.Tooltips.Healthcheck.Host} + name="healthcheck.host" + onBlur={formik.handleBlur} + onChange={formik.handleChange} + placeholder="example.org" + value={formik.values.healthcheck.host} + /> + )} Date: Tue, 2 Jan 2024 16:09:10 -0500 Subject: [PATCH 10/14] fix drawer showing as edit mode after deletion --- .../LoadBalancerServiceTargets.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/LoadBalancerServiceTargets.tsx b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/LoadBalancerServiceTargets.tsx index c8b0e318bee..14266e52996 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/LoadBalancerServiceTargets.tsx +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/LoadBalancerServiceTargets.tsx @@ -37,9 +37,9 @@ export const LoadBalancerServiceTargets = () => { const [isDrawerOpen, setIsDrawerOpen] = useState(false); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const [ - selectedServiceTarget, - setSelectedServiceTarget, - ] = useState(); + selectedServiceTargetId, + setSelectedServiceTargetId, + ] = useState(); const pagination = usePagination(1, PREFERENCE_KEY); @@ -58,12 +58,12 @@ export const LoadBalancerServiceTargets = () => { const handleEditServiceTarget = (serviceTarget: ServiceTarget) => { setIsDrawerOpen(true); - setSelectedServiceTarget(serviceTarget); + setSelectedServiceTargetId(serviceTarget.id); }; const handleDeleteServiceTarget = (serviceTarget: ServiceTarget) => { setIsDeleteDialogOpen(true); - setSelectedServiceTarget(serviceTarget); + setSelectedServiceTargetId(serviceTarget.id); }; // If the user types in a search query, filter results by label. @@ -80,6 +80,10 @@ export const LoadBalancerServiceTargets = () => { filter ); + const selectedServiceTarget = data?.data.find( + (serviceTarget) => serviceTarget.id === selectedServiceTargetId + ); + if (isLoading) { return ; } @@ -192,7 +196,7 @@ export const LoadBalancerServiceTargets = () => { { setIsDrawerOpen(false); - setSelectedServiceTarget(undefined); + setSelectedServiceTargetId(undefined); }} loadbalancerId={Number(loadbalancerId)} open={isDrawerOpen} From bfbb2bc65dc0264e8a55a07594af704ea7782934 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Tue, 2 Jan 2024 16:22:03 -0500 Subject: [PATCH 11/14] pass null for path and host in preporation for API change --- .../ServiceTargets/utils.test.ts | 52 +++++++++++++++++++ .../ServiceTargets/utils.ts | 16 +++--- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/utils.test.ts b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/utils.test.ts index 8bb7350c3f0..dab00d5fded 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/utils.test.ts +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/utils.test.ts @@ -52,4 +52,56 @@ describe('getNormalizedServiceTargetPayload', () => { protocol: 'https', }); }); + + it('should make the healthcheck host and path null for tcp', () => { + expect( + getNormalizedServiceTargetPayload({ + certificate_id: null, + endpoints: [ + { + host: '', + ip: '139.144.129.228', + port: 80, + rate_capacity: 10000, + }, + ], + healthcheck: { + healthy_threshold: 3, + host: 'example.com', + interval: 10, + path: '/test', + protocol: 'tcp', + timeout: 5, + unhealthy_threshold: 3, + }, + label: 'test', + load_balancing_policy: 'round_robin', + percentage: 10, + protocol: 'https', + }) + ).toStrictEqual({ + certificate_id: null, + endpoints: [ + { + host: null, + ip: '139.144.129.228', + port: 80, + rate_capacity: 10000, + }, + ], + healthcheck: { + healthy_threshold: 3, + host: null, + interval: 10, + path: null, + protocol: 'tcp', + timeout: 5, + unhealthy_threshold: 3, + }, + label: 'test', + load_balancing_policy: 'round_robin', + percentage: 10, + protocol: 'https', + }); + }); }); diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/utils.ts b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/utils.ts index 82277317e3d..ec8e07148bb 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/utils.ts +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/utils.ts @@ -14,11 +14,15 @@ export const getNormalizedServiceTargetPayload = ( })), healthcheck: { ...serviceTarget.healthcheck, - host: serviceTarget.healthcheck.host - ? serviceTarget.healthcheck.host - : null, - path: serviceTarget.healthcheck.path - ? serviceTarget.healthcheck.path - : null, + host: + serviceTarget.healthcheck.host && + serviceTarget.healthcheck.protocol === 'http' + ? serviceTarget.healthcheck.host + : null, + path: + serviceTarget.healthcheck.path && + serviceTarget.healthcheck.protocol === 'http' + ? serviceTarget.healthcheck.path + : null, }, }); From 63abe7e0fcace11ed3e4dd6f00e8cb6ce34d64f9 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Tue, 2 Jan 2024 16:52:16 -0500 Subject: [PATCH 12/14] allow host to be null for tcp healthchecks --- packages/validation/src/loadbalancers.schema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/validation/src/loadbalancers.schema.ts b/packages/validation/src/loadbalancers.schema.ts index eda9069b1fa..aacafbfa066 100644 --- a/packages/validation/src/loadbalancers.schema.ts +++ b/packages/validation/src/loadbalancers.schema.ts @@ -65,7 +65,7 @@ const HealthCheckSchema = object({ path: string().nullable(), host: string().when('protocol', { is: 'tcp', - then: (o) => o.notRequired(), + then: (o) => o.nullable(), otherwise: (o) => o.required('Health Check host is required.'), }), }); From cb17f8605638c5b2d48238f70fa2f0692c64e55e Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Tue, 2 Jan 2024 17:45:57 -0500 Subject: [PATCH 13/14] fix healthcheck protocol toolip icon alignment --- .../LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx | 2 +- .../LoadBalancerDetail/ServiceTargets/ServiceTargetDrawer.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx b/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx index 62f9946004c..821a6ae2e30 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerCreate/ServiceTargetForm.tsx @@ -220,7 +220,7 @@ export const ServiceTargetForm = (props: Props) => { sx={{ marginBottom: '0px !important' }} value={formik.values.healthcheck.protocol} > - + Protocol { sx={{ marginBottom: '0px !important' }} value={formik.values.healthcheck.protocol} > - + Protocol Date: Fri, 5 Jan 2024 11:30:20 -0500 Subject: [PATCH 14/14] remove `Host` as a rule match type option --- packages/api-v4/src/aglb/types.ts | 7 ++++++- .../Routes/MatchTypeInfo.tsx | 18 +++++++++--------- .../LoadBalancerDetail/Routes/utils.ts | 11 +++++++---- .../validation/src/loadbalancers.schema.ts | 14 ++++++++++---- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/packages/api-v4/src/aglb/types.ts b/packages/api-v4/src/aglb/types.ts index 7dcae7df052..ab3795cc682 100644 --- a/packages/api-v4/src/aglb/types.ts +++ b/packages/api-v4/src/aglb/types.ts @@ -42,7 +42,12 @@ type Policy = | 'random' | 'maglev'; -export type MatchField = 'path_prefix' | 'query' | 'host' | 'header' | 'method'; +export type MatchField = + | 'always_match' + | 'path_prefix' + | 'query' + | 'header' + | 'method'; export interface RoutePayload { label: string; diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/Routes/MatchTypeInfo.tsx b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/Routes/MatchTypeInfo.tsx index b9688e18d83..a09e006761a 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/Routes/MatchTypeInfo.tsx +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/Routes/MatchTypeInfo.tsx @@ -5,6 +5,15 @@ import React from 'react'; export const MatchTypeInfo = () => { const types = [ + { + description: + 'Match is based on both the name of a HTTP header and its value.', + title: 'HTTP Header', + }, + { + description: 'Match is on the request method.', + title: 'HTTP Method', + }, { description: 'Match is on a network path.', title: 'Path', @@ -14,15 +23,6 @@ export const MatchTypeInfo = () => { 'Match is based on both the name of the query and the single URL query value to match on.', title: 'Query String', }, - { - description: - 'Match is based on both the name of a HTTP header and its value.', - title: 'HTTP Header', - }, - { - description: 'Match is on the request method.', - title: 'Method', - }, ]; return ( diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/Routes/utils.ts b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/Routes/utils.ts index 21d71f52f08..fbe3cf5f49a 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/Routes/utils.ts +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/Routes/utils.ts @@ -9,17 +9,20 @@ import type { RulePayload, } from '@linode/api-v4'; -export const matchFieldMap: Record = { +type CustomerFacingMatchFieldOption = Exclude; + +export const matchFieldMap: Record = { header: 'HTTP Header', - host: 'Host', method: 'HTTP Method', path_prefix: 'Path', query: 'Query String', }; -export const matchValuePlaceholder: Record = { +export const matchValuePlaceholder: Record< + CustomerFacingMatchFieldOption, + string +> = { header: 'x-my-header=this', - host: 'example.com', method: 'POST', path_prefix: '/my-path', query: '?my-query-param=this', diff --git a/packages/validation/src/loadbalancers.schema.ts b/packages/validation/src/loadbalancers.schema.ts index aacafbfa066..783ed02234e 100644 --- a/packages/validation/src/loadbalancers.schema.ts +++ b/packages/validation/src/loadbalancers.schema.ts @@ -2,6 +2,14 @@ import { number, object, string, array } from 'yup'; const LABEL_REQUIRED = 'Label is required.'; +const matchFieldOptions = [ + 'always_match', + 'path_prefix', + 'query', + 'header', + 'method', +]; + export const CreateCertificateSchema = object({ certificate: string().required('Certificate is required.'), key: string().when('type', { @@ -119,7 +127,7 @@ const TCPMatchConditionSchema = object({ const HTTPMatchConditionSchema = TCPMatchConditionSchema.concat( object({ match_field: string() - .oneOf(['path_prefix', 'query', 'header', 'method', 'host']) + .oneOf(matchFieldOptions) .required('Match field is required.'), match_value: string().required('Match value is required.'), session_stickiness_cookie: string().nullable(), @@ -237,9 +245,7 @@ const CreateLoadBalancerServiceTargetSchema = object({ const CreateLoadBalancerRuleSchema = object({ match_condition: object().shape({ hostname: string().required(), - match_field: string() - .oneOf(['path_prefix', 'host', 'query', 'hostname', 'header', 'method']) - .required(), + match_field: string().oneOf(matchFieldOptions).required(), match_value: string().required(), session_stickiness_cookie: string(), session_stickiness_ttl: number().integer(),