@@ -2,6 +2,7 @@ import { getLinodeInterfaces } from '@linode/api-v4';
2
2
import {
3
3
useAddFirewallDeviceMutation ,
4
4
useAllFirewallsQuery ,
5
+ useAllLinodesQuery ,
5
6
useGrants ,
6
7
useProfile ,
7
8
} from '@linode/queries' ;
@@ -18,6 +19,7 @@ import { SupportLink } from 'src/components/SupportLink';
18
19
import { getLinodeInterfaceType } from 'src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/utilities' ;
19
20
import { getAPIErrorOrDefault } from 'src/utilities/errorUtils' ;
20
21
import { getEntityIdsByPermission } from 'src/utilities/grants' ;
22
+ import { useIsLinodeInterfacesEnabled } from 'src/utilities/linodes' ;
21
23
import { sanitizeHTML } from 'src/utilities/sanitizeHTML' ;
22
24
23
25
import type { Linode , LinodeInterface } from '@linode/api-v4' ;
@@ -54,9 +56,12 @@ export const AddLinodeDrawer = (props: Props) => {
54
56
55
57
const { data : grants } = useGrants ( ) ;
56
58
const { data : profile } = useProfile ( ) ;
59
+ const { isLinodeInterfacesEnabled } = useIsLinodeInterfacesEnabled ( ) ;
57
60
const isRestrictedUser = Boolean ( profile ?. restricted ) ;
58
61
59
62
const { data, error, isLoading } = useAllFirewallsQuery ( ) ;
63
+ const { data : allLinodes } = useAllLinodesQuery ( { } , { } ) ;
64
+ const [ linodeOptions , setLinodeOptions ] = React . useState < Linode [ ] > ( [ ] ) ;
60
65
61
66
const firewall = data ?. find ( ( firewall ) => firewall . id === Number ( id ) ) ;
62
67
@@ -214,17 +219,15 @@ export const AddLinodeDrawer = (props: Props) => {
214
219
? getEntityIdsByPermission ( grants , 'linode' , 'read_only' )
215
220
: [ ] ;
216
221
217
- const assignedLinodes = data
218
- ?. map ( ( firewall ) => firewall . entities )
219
- . flat ( )
220
- ?. filter ( ( service ) => service . type === 'linode' ) ;
221
-
222
- const linodeOptionsFilter = ( linode : Linode ) => {
223
- return (
224
- ! readOnlyLinodeIds . includes ( linode . id ) &&
225
- ! assignedLinodes ?. some ( ( service ) => service . id === linode . id )
226
- ) ;
227
- } ;
222
+ const firewallEntities = data ?. map ( ( firewall ) => firewall . entities ) . flat ( ) ;
223
+ const assignedLinodes = firewallEntities ?. filter (
224
+ ( service ) => service . type === 'linode'
225
+ ) ;
226
+ const assignedInterfaceIds = new Set < number > (
227
+ firewallEntities
228
+ ?. filter ( ( service ) => service . type === 'interface' )
229
+ ?. map ( ( service ) => service . id ) ?? [ ]
230
+ ) ;
228
231
229
232
const onSelectionChange = async ( linodes : Linode [ ] ) => {
230
233
setLocalError ( '' ) ;
@@ -233,10 +236,10 @@ export const AddLinodeDrawer = (props: Props) => {
233
236
const _interfacesToAddMap = new Map < number , InterfaceDeviceInfo | null > ( ) ;
234
237
235
238
for ( const linode of linodes ) {
236
- if ( linode . interface_generation === 'legacy_config' ) {
237
- legacyLinodes . push ( linode ) ;
238
- } else {
239
+ if ( linode . interface_generation === 'linode' ) {
239
240
interfaceLinodes . push ( linode ) ;
241
+ } else {
242
+ legacyLinodes . push ( linode ) ;
240
243
}
241
244
}
242
245
@@ -247,19 +250,19 @@ export const AddLinodeDrawer = (props: Props) => {
247
250
const linodeId = linode . id ;
248
251
const interfaces = await getLinodeInterfaces ( linodeId ) ;
249
252
// vlan interfaces cannot have a firewall assigned to them
250
- const nonVlanInterfaces = interfaces . interfaces . filter (
251
- ( iface ) => ! iface . vlan
253
+ const assignableInterfaces = interfaces . interfaces . filter (
254
+ ( iface ) => ! iface . vlan && ! assignedInterfaceIds . has ( iface . id )
252
255
) ;
253
256
254
- if ( nonVlanInterfaces . length === 1 ) {
257
+ if ( assignableInterfaces . length === 1 ) {
255
258
_interfacesToAddMap . set ( linodeId , {
256
259
linodeId,
257
260
linodeLabel : linode . label ,
258
- interfaceId : nonVlanInterfaces [ 0 ] . id ,
261
+ interfaceId : assignableInterfaces [ 0 ] . id ,
259
262
} ) ;
260
263
}
261
264
262
- if ( nonVlanInterfaces . length > 1 ) {
265
+ if ( assignableInterfaces . length > 1 ) {
263
266
if ( ! interfacesToAddMap . has ( linodeId ) ) {
264
267
_interfacesToAddMap . set ( linodeId , null ) ;
265
268
} else {
@@ -269,7 +272,7 @@ export const AddLinodeDrawer = (props: Props) => {
269
272
) ;
270
273
}
271
274
272
- const interfacesWithLabels = nonVlanInterfaces . map ( ( iface ) => ( {
275
+ const interfacesWithLabels = assignableInterfaces . map ( ( iface ) => ( {
273
276
...iface ,
274
277
label : `${ getLinodeInterfaceType ( iface ) } Interface (ID : ${ iface . id } )` ,
275
278
value : iface . id ,
@@ -294,12 +297,6 @@ export const AddLinodeDrawer = (props: Props) => {
294
297
setInterfacesToAddMap ( _interfacesToAddMap ) ;
295
298
} ;
296
299
297
- React . useEffect ( ( ) => {
298
- if ( error ) {
299
- setLocalError ( 'Could not load firewall data' ) ;
300
- }
301
- } , [ error ] ) ;
302
-
303
300
const handleClose = ( ) => {
304
301
setLinodesToAdd ( [ ] ) ;
305
302
setInterfacesToAddMap ( new Map ( ) ) ;
@@ -308,6 +305,49 @@ export const AddLinodeDrawer = (props: Props) => {
308
305
onClose ( ) ;
309
306
} ;
310
307
308
+ React . useEffect ( ( ) => {
309
+ const linodeOptionsFilter = async ( linode : Linode ) => {
310
+ if ( linode . interface_generation === 'linode' ) {
311
+ const interfaces = await getLinodeInterfaces ( linode . id ) ;
312
+ // Return true if Linode has some non-vlan interface that is not assigned to a firewall
313
+ return (
314
+ ! readOnlyLinodeIds . includes ( linode . id ) &&
315
+ interfaces . interfaces . some (
316
+ ( iface ) => ! iface . vlan && ! assignedInterfaceIds . has ( iface . id )
317
+ )
318
+ ) ;
319
+ }
320
+ return (
321
+ ! readOnlyLinodeIds . includes ( linode . id ) &&
322
+ ! assignedLinodes ?. some ( ( service ) => service . id === linode . id )
323
+ ) ;
324
+ } ;
325
+
326
+ const filterLinodes = async ( ) => {
327
+ const asyncFilteredDataPromises = allLinodes ?. map ( ( linode ) =>
328
+ linodeOptionsFilter ( linode )
329
+ ) ;
330
+ const predicateArr = await Promise . all ( asyncFilteredDataPromises ?? [ ] ) ;
331
+ const filteredLinodes = allLinodes ?. filter ( ( _ , idx ) => predicateArr [ idx ] ) ;
332
+
333
+ setLinodeOptions ( filteredLinodes ?? [ ] ) ;
334
+ } ;
335
+
336
+ filterLinodes ( ) ;
337
+ // eslint-disable-next-line react-hooks/exhaustive-deps
338
+ } , [
339
+ allLinodes ?. length ,
340
+ assignedLinodes ?. length ,
341
+ readOnlyLinodeIds ?. length ,
342
+ assignedInterfaceIds . size ,
343
+ ] ) ;
344
+
345
+ React . useEffect ( ( ) => {
346
+ if ( error ) {
347
+ setLocalError ( 'Could not load firewall data' ) ;
348
+ }
349
+ } , [ error ] ) ;
350
+
311
351
return (
312
352
< Drawer
313
353
NotFoundComponent = { NotFound }
@@ -327,42 +367,43 @@ export const AddLinodeDrawer = (props: Props) => {
327
367
helperText = { helperText }
328
368
multiple
329
369
onSelectionChange = { ( linodes ) => onSelectionChange ( linodes ) }
330
- optionsFilter = { linodeOptionsFilter }
370
+ options = { linodeOptions }
331
371
value = { [
332
372
...linodesToAdd . map ( ( linode ) => linode . id ) ,
333
373
...Array . from ( interfacesToAddMap . keys ( ) ) ,
334
374
] }
335
375
/>
336
- { linodesWithMultiInterfaces . length > 0 && (
376
+ { isLinodeInterfacesEnabled && linodesWithMultiInterfaces . length > 0 && (
337
377
< Typography marginTop = { 3 } >
338
378
{ `The following ${ linodesWithMultiInterfaces . length === 1 ? 'Linode has' : 'Linodes have' }
339
379
more than one interface to which a firewall can be applied. Select which interface.` }
340
380
</ Typography >
341
381
) }
342
- { linodesWithMultiInterfaces . map ( ( linode ) => (
343
- < Select
344
- key = { linode . linodeId }
345
- label = { `${ linode . linodeLabel } Interface` }
346
- onChange = { ( e , option ) => {
347
- const updatedInterfacesToAdd = new Map ( interfacesToAddMap ) ;
348
- updatedInterfacesToAdd . set ( linode . linodeId , {
349
- linodeId : linode . linodeId ,
350
- linodeLabel : linode . linodeLabel ,
351
- interfaceId : option . value ,
352
- } ) ;
353
- setInterfacesToAddMap ( updatedInterfacesToAdd ) ;
354
- } }
355
- options = { linode . linodeInterfaces }
356
- placeholder = "Select Interface"
357
- value = {
358
- linode . linodeInterfaces . find (
359
- ( iface ) =>
360
- iface . id ===
361
- interfacesToAddMap . get ( linode . linodeId ) ?. interfaceId
362
- ) ?? null
363
- }
364
- />
365
- ) ) }
382
+ { isLinodeInterfacesEnabled &&
383
+ linodesWithMultiInterfaces . map ( ( linode ) => (
384
+ < Select
385
+ key = { linode . linodeId }
386
+ label = { `${ linode . linodeLabel } Interface` }
387
+ onChange = { ( e , option ) => {
388
+ const updatedInterfacesToAdd = new Map ( interfacesToAddMap ) ;
389
+ updatedInterfacesToAdd . set ( linode . linodeId , {
390
+ linodeId : linode . linodeId ,
391
+ linodeLabel : linode . linodeLabel ,
392
+ interfaceId : option . value ,
393
+ } ) ;
394
+ setInterfacesToAddMap ( updatedInterfacesToAdd ) ;
395
+ } }
396
+ options = { linode . linodeInterfaces }
397
+ placeholder = "Select Interface"
398
+ value = {
399
+ linode . linodeInterfaces . find (
400
+ ( iface ) =>
401
+ iface . id ===
402
+ interfacesToAddMap . get ( linode . linodeId ) ?. interfaceId
403
+ ) ?? null
404
+ }
405
+ />
406
+ ) ) }
366
407
< ActionsPanel
367
408
primaryButtonProps = { {
368
409
disabled :
0 commit comments