From 5e75978d436feb6dc8d4a92cb16f0eefff01a6dc Mon Sep 17 00:00:00 2001 From: Connie Liu Date: Fri, 16 May 2025 12:33:15 -0400 Subject: [PATCH 1/8] only disable button if entity has active firewall --- .../LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx | 9 +++++++-- .../NodeBalancerDetail/NodeBalancerFirewalls.tsx | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx index 6175971d01a..dc338cb38ff 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx @@ -86,9 +86,14 @@ export const LinodeFirewalls = (props: LinodeFirewallsProps) => { diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx index 5866fe61d69..2d1dba59472 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx @@ -112,14 +112,19 @@ export const NodeBalancerFirewalls = (props: Props) => { From 4a4548a7bf45ee12bb7aa33f7aa4814e3fc31719 Mon Sep 17 00:00:00 2001 From: Connie Liu Date: Fri, 16 May 2025 12:55:21 -0400 Subject: [PATCH 2/8] filter firewalls --- .../Firewalls/components/FirewallSelect.tsx | 24 +++++++++++++++---- .../LinodeFirewalls/AddFirewallForm.tsx | 10 ++++++-- .../LinodeFirewalls/LinodeFirewalls.tsx | 1 + .../NodeBalancerFirewalls.tsx | 1 + 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/packages/manager/src/features/Firewalls/components/FirewallSelect.tsx b/packages/manager/src/features/Firewalls/components/FirewallSelect.tsx index 3e0ae7cf68d..25313578e4b 100644 --- a/packages/manager/src/features/Firewalls/components/FirewallSelect.tsx +++ b/packages/manager/src/features/Firewalls/components/FirewallSelect.tsx @@ -28,9 +28,14 @@ interface Props label?: string; /** * Optionally pass your own array of Firewalls. - * All Firewall will show if this is omitted. + * All Firewalls will show if this is omitted. */ options?: Firewall[]; + /** + * Determine which firewalls should be available as options. + * If the options prop is used, this is filter ignored. + */ + optionsFilter?: (firewall: Firewall) => boolean; /** * The ID of the selected Firewall */ @@ -47,9 +52,20 @@ interface Props export const FirewallSelect = ( props: Props ) => { - const { errorText, hideDefaultChips, loading, value, ...rest } = props; + const { + errorText, + hideDefaultChips, + loading, + value, + options, + optionsFilter, + ...rest + } = props; + + const { data: firewalls, error, isLoading } = useAllFirewallsQuery(!options); - const { data: firewalls, error, isLoading } = useAllFirewallsQuery(); + const firewallOptions = + options || (optionsFilter ? firewalls?.filter(optionsFilter) : firewalls); const { defaultNumEntities, isDefault, tooltipText } = useDefaultFirewallChipInformation(value, hideDefaultChips); @@ -65,7 +81,7 @@ export const FirewallSelect = ( label="Firewall" loading={isLoading || loading} noMarginTop - options={firewalls ?? []} + options={firewallOptions ?? []} placeholder="None" renderOption={({ key, ...props }, option, state) => ( void; } export const AddFirewallForm = (props: Props) => { - const { entityId, entityType, onCancel } = props; + const { attachedFirewalls, entityId, entityType, onCancel } = props; const { enqueueSnackbar } = useSnackbar(); const entityLabel = formattedTypes[entityType] ?? entityType; @@ -51,6 +52,10 @@ export const AddFirewallForm = (props: Props) => { } }; + const optionsFilter = (firewall: Firewall) => { + return !attachedFirewalls?.some((fw) => fw.id === firewall.id); + }; + return (
@@ -66,6 +71,7 @@ export const AddFirewallForm = (props: Props) => { errorText={fieldState.error?.message} label="Firewall" onChange={(e, value) => field.onChange(value?.id)} + optionsFilter={optionsFilter} placeholder="Select a Firewall" textFieldProps={{ inputRef: field.ref, diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx index dc338cb38ff..aed75ee8b4c 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx @@ -123,6 +123,7 @@ export const LinodeFirewalls = (props: LinodeFirewallsProps) => { title="Add Firewall" > setIsAddFirewalDrawerOpen(false)} diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx index 2d1dba59472..da06b32a607 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx @@ -173,6 +173,7 @@ export const NodeBalancerFirewalls = (props: Props) => { title="Add Firewall" > From d7b5c7dd20ed4042835955dd04e798f0f7580fa7 Mon Sep 17 00:00:00 2001 From: Connie Liu Date: Fri, 16 May 2025 13:12:48 -0400 Subject: [PATCH 3/8] which firewall to display --- .../LinodeInterfaces/LinodeInterfaceFirewall.tsx | 3 ++- .../NodeBalancerSummary/SummaryPanel.tsx | 15 ++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceFirewall.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceFirewall.tsx index 48c03ca0765..fc909c1968b 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceFirewall.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceFirewall.tsx @@ -27,7 +27,8 @@ export const LinodeInterfaceFirewall = ({ interfaceId, linodeId }: Props) => { return 'None'; } - const firewall = data.data[0]; + const firewall = + data.data.find((firewall) => firewall.status === 'enabled') ?? data.data[0]; return {firewall.label}; }; diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx index 175a3373d90..2112e2baa28 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx @@ -38,13 +38,14 @@ export const SummaryPanel = () => { const { data: attachedFirewallData } = useNodeBalancersFirewallsQuery( Number(id) ); - const linkText = attachedFirewallData?.data[0]?.label; - const linkID = attachedFirewallData?.data[0]?.id; + const firewallToDisplay = // display either the enabled firewall or, if none are enabled, the first firewall + attachedFirewallData?.data.find( + (firewall) => firewall.status === 'enabled' + ) ?? attachedFirewallData?.data[0]; const region = regions?.find((r) => r.id === nodebalancer?.region); const { mutateAsync: updateNodeBalancer } = useNodebalancerUpdateMutation( Number(id) ); - const displayFirewallLink = !!attachedFirewallData?.data?.length; const isNodeBalancerReadOnly = useIsResourceRestricted({ grantLevel: 'read_only', @@ -161,18 +162,18 @@ export const SummaryPanel = () => { - {displayFirewallLink && ( + {firewallToDisplay && ( Firewall - {linkText} + {firewallToDisplay.label} From d57753acd04d6afddd10944837186ecf87264127 Mon Sep 17 00:00:00 2001 From: Connie Liu Date: Fri, 16 May 2025 13:27:05 -0400 Subject: [PATCH 4/8] reminder to myself --- .../EditInterfaceDrawer/EditInterfaceForm.utils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/EditInterfaceDrawer/EditInterfaceForm.utils.ts b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/EditInterfaceDrawer/EditInterfaceForm.utils.ts index 8e2ddcec2f4..151e1ec8ed3 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/EditInterfaceDrawer/EditInterfaceForm.utils.ts +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/EditInterfaceDrawer/EditInterfaceForm.utils.ts @@ -53,6 +53,8 @@ export const useUpdateLinodeInterfaceFirewallMutation = ( ._ctx.interfaces._ctx.interface(interfaceId)._ctx.firewalls ); + // todo connie: think about what we want to do here + // >> assign multiple firewalls at once? assign one firewall at a time but replace the enabled one only? // For now, assume the first Firewall is the Interface's firewall. // (we are not supporting multiple firewalls per interface right now) const currentFirewall = interfaceFirewalls.data[0] as From 25b24b0dfbaf88723231ae6c3626394f6b032a99 Mon Sep 17 00:00:00 2001 From: Connie Liu Date: Fri, 16 May 2025 16:03:46 -0400 Subject: [PATCH 5/8] use show more instead --- .../LinodeFirewalls/LinodeFirewalls.tsx | 7 ---- .../EditInterfaceForm.utils.ts | 2 -- .../LinodeInterfaceFirewall.tsx | 32 +++++++++++++++++-- .../NodeBalancerFirewalls.tsx | 7 ---- .../NodeBalancerSummary/SummaryPanel.tsx | 31 +++++++++--------- 5 files changed, 46 insertions(+), 33 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx index aed75ee8b4c..b4ff1630515 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx @@ -86,14 +86,7 @@ export const LinodeFirewalls = (props: LinodeFirewallsProps) => { diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/EditInterfaceDrawer/EditInterfaceForm.utils.ts b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/EditInterfaceDrawer/EditInterfaceForm.utils.ts index 151e1ec8ed3..8e2ddcec2f4 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/EditInterfaceDrawer/EditInterfaceForm.utils.ts +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/EditInterfaceDrawer/EditInterfaceForm.utils.ts @@ -53,8 +53,6 @@ export const useUpdateLinodeInterfaceFirewallMutation = ( ._ctx.interfaces._ctx.interface(interfaceId)._ctx.firewalls ); - // todo connie: think about what we want to do here - // >> assign multiple firewalls at once? assign one firewall at a time but replace the enabled one only? // For now, assume the first Firewall is the Interface's firewall. // (we are not supporting multiple firewalls per interface right now) const currentFirewall = interfaceFirewalls.data[0] as diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceFirewall.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceFirewall.tsx index fc909c1968b..8d839d3ebda 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceFirewall.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceFirewall.tsx @@ -1,7 +1,9 @@ import { useLinodeInterfaceFirewallsQuery } from '@linode/queries'; +import { Stack } from '@linode/ui'; import React from 'react'; import { Link } from 'src/components/Link'; +import { ShowMore } from 'src/components/ShowMore/ShowMore'; import { Skeleton } from 'src/components/Skeleton'; interface Props { @@ -27,8 +29,34 @@ export const LinodeInterfaceFirewall = ({ interfaceId, linodeId }: Props) => { return 'None'; } - const firewall = + // display the enabled firewall if it exists, otherwise display the first firewall + const displayedFirewall = data.data.find((firewall) => firewall.status === 'enabled') ?? data.data[0]; - return {firewall.label}; + const firewalls = data.data.filter( + (firewall) => firewall.id !== displayedFirewall.id + ); + + return ( + + + {displayedFirewall.label} + + {firewalls.length > 0 && ( + ( + + {firewalls.map((firewall) => ( + + {firewall.label} + + ))} + + )} + /> + )} + + ); }; diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx index da06b32a607..737d047e880 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx @@ -112,19 +112,12 @@ export const NodeBalancerFirewalls = (props: Props) => { diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx index 2112e2baa28..e0fe57ccd8e 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx @@ -38,10 +38,7 @@ export const SummaryPanel = () => { const { data: attachedFirewallData } = useNodeBalancersFirewallsQuery( Number(id) ); - const firewallToDisplay = // display either the enabled firewall or, if none are enabled, the first firewall - attachedFirewallData?.data.find( - (firewall) => firewall.status === 'enabled' - ) ?? attachedFirewallData?.data[0]; + const firewalls = attachedFirewallData?.data ?? []; const region = regions?.find((r) => r.id === nodebalancer?.region); const { mutateAsync: updateNodeBalancer } = useNodebalancerUpdateMutation( Number(id) @@ -162,20 +159,24 @@ export const SummaryPanel = () => { - {firewallToDisplay && ( + {firewalls.length > 0 && ( - Firewall + {firewalls.length > 1 ? 'Firewalls' : 'Firewall'} - - - {firewallToDisplay.label} - - + {firewalls.map((firewall) => { + return ( + + + {firewall.label} + + + ); + })} )} From 9f49e9be72c8fb252051ba2b44365869d582bf30 Mon Sep 17 00:00:00 2001 From: Connie Liu Date: Fri, 16 May 2025 16:36:30 -0400 Subject: [PATCH 6/8] filter out ineligible firewalls?? --- .../LinodeNetworking/LinodeFirewalls/AddFirewallForm.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/AddFirewallForm.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/AddFirewallForm.tsx index fa2ec026119..bed3f87b79d 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/AddFirewallForm.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/AddFirewallForm.tsx @@ -30,6 +30,10 @@ interface Props { export const AddFirewallForm = (props: Props) => { const { attachedFirewalls, entityId, entityType, onCancel } = props; + const hasEnabledFirewall = attachedFirewalls?.some( + (firewall) => firewall.status === 'enabled' + ); + const { enqueueSnackbar } = useSnackbar(); const entityLabel = formattedTypes[entityType] ?? entityType; @@ -53,7 +57,10 @@ export const AddFirewallForm = (props: Props) => { }; const optionsFilter = (firewall: Firewall) => { - return !attachedFirewalls?.some((fw) => fw.id === firewall.id); + return ( + !(hasEnabledFirewall && firewall.status === 'enabled') && + !attachedFirewalls?.some((fw) => fw.id === firewall.id) + ); }; return ( From 0408ccc977e05bf528669d0a1e7c912046199f0e Mon Sep 17 00:00:00 2001 From: Connie Liu Date: Fri, 16 May 2025 16:42:22 -0400 Subject: [PATCH 7/8] jk moving this to m3-9829 --- .../LinodeInterfaceFirewall.tsx | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceFirewall.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceFirewall.tsx index 8d839d3ebda..48c03ca0765 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceFirewall.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceFirewall.tsx @@ -1,9 +1,7 @@ import { useLinodeInterfaceFirewallsQuery } from '@linode/queries'; -import { Stack } from '@linode/ui'; import React from 'react'; import { Link } from 'src/components/Link'; -import { ShowMore } from 'src/components/ShowMore/ShowMore'; import { Skeleton } from 'src/components/Skeleton'; interface Props { @@ -29,34 +27,7 @@ export const LinodeInterfaceFirewall = ({ interfaceId, linodeId }: Props) => { return 'None'; } - // display the enabled firewall if it exists, otherwise display the first firewall - const displayedFirewall = - data.data.find((firewall) => firewall.status === 'enabled') ?? data.data[0]; + const firewall = data.data[0]; - const firewalls = data.data.filter( - (firewall) => firewall.id !== displayedFirewall.id - ); - - return ( - - - {displayedFirewall.label} - - {firewalls.length > 0 && ( - ( - - {firewalls.map((firewall) => ( - - {firewall.label} - - ))} - - )} - /> - )} - - ); + return {firewall.label}; }; From 72d718ba8c294173202b40d053db642813463085 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Tue, 20 May 2025 11:35:12 -0400 Subject: [PATCH 8/8] Added changeset: Support for Linodes and NodeBalancers to have multiple firewalls --- packages/manager/.changeset/pr-12241-added-1747755312168.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/manager/.changeset/pr-12241-added-1747755312168.md diff --git a/packages/manager/.changeset/pr-12241-added-1747755312168.md b/packages/manager/.changeset/pr-12241-added-1747755312168.md new file mode 100644 index 00000000000..b5ca2fcb9d8 --- /dev/null +++ b/packages/manager/.changeset/pr-12241-added-1747755312168.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Added +--- + +Support for Linodes and NodeBalancers to have multiple firewalls ([#12241](https://github.com/linode/manager/pull/12241))