Skip to content

Commit 5fd0f8a

Browse files
change: [UIE-9888] - Display front end IP and backend VPCs for Nodebalancer (#13394)
1 parent 41cfd20 commit 5fd0f8a

File tree

17 files changed

+418
-197
lines changed

17 files changed

+418
-197
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/api-v4": Changed
3+
---
4+
5+
New fields in the NodeBalancer details object and NodeBalancerVPC object to align with recent API updates ([#13394](https://github.com/linode/manager/pull/13394))

packages/api-v4/src/nodebalancers/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type UDPStickiness = 'none' | 'session' | 'source_ip';
1010

1111
export type Stickiness = TCPStickiness | UDPStickiness;
1212

13-
type NodeBalancerType = 'common' | 'premium';
13+
type NodeBalancerType = 'common' | 'premium' | 'premium_40GB';
1414

1515
export interface LKEClusterInfo {
1616
id: number;
@@ -33,6 +33,8 @@ export interface NodeBalancer {
3333
*/
3434
client_udp_sess_throttle?: number;
3535
created: string;
36+
frontend_address_type: 'public' | 'vpc';
37+
frontend_vpc_subnet_id: null | number;
3638
hostname: string;
3739
id: number;
3840
ipv4: string;
@@ -145,6 +147,7 @@ export interface NodeBalancerVpcConfig {
145147
ipv4_range: null | string;
146148
ipv6_range: null | string;
147149
nodebalancer_id: number;
150+
purpose: 'backend' | 'frontend';
148151
subnet_id: number;
149152
vpc_id: number;
150153
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Changed
3+
---
4+
5+
Display front end IP and backend VPCs for Nodebalancer ([#13394](https://github.com/linode/manager/pull/13394))

packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.test.tsx

Lines changed: 75 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {
22
nodeBalancerConfigFactory,
3-
nodeBalancerConfigVPCFactory,
43
nodeBalancerFactory,
4+
nodeBalancerVPCFactory,
55
} from '@linode/utilities';
66
import { waitFor } from '@testing-library/react';
77
import * as React from 'react';
@@ -13,12 +13,10 @@ import { renderWithTheme } from 'src/utilities/testHelpers';
1313
import { SummaryPanel } from './SummaryPanel';
1414

1515
const queryMocks = vi.hoisted(() => ({
16-
useAllNodeBalancerConfigsQuery: vi.fn().mockReturnValue({ data: undefined }),
17-
useNodeBalancerQuery: vi.fn().mockReturnValue({ data: undefined }),
18-
useNodeBalancersFirewallsQuery: vi.fn().mockReturnValue({ data: undefined }),
19-
useNodeBalancerVPCConfigsBetaQuery: vi
20-
.fn()
21-
.mockReturnValue({ data: undefined }),
16+
useAllNodeBalancerConfigsQuery: vi.fn().mockReturnValue({ data: null }),
17+
useNodeBalancerQuery: vi.fn().mockReturnValue({ data: null }),
18+
useNodeBalancersFirewallsQuery: vi.fn().mockReturnValue({ data: null }),
19+
useNodeBalancerVPCConfigsBetaQuery: vi.fn().mockReturnValue({ data: null }),
2220
useParams: vi.fn().mockReturnValue({}),
2321
userPermissions: vi.fn(() => ({
2422
data: {
@@ -51,8 +49,8 @@ vi.mock('@linode/queries', async () => {
5149
};
5250
});
5351

54-
const nodeBalancerDetails = 'NodeBalancer Details';
55-
const nbVpcConfig = nodeBalancerConfigVPCFactory.build();
52+
const nodeBalancerDetails = 'Details';
53+
const nbVpcConfig = nodeBalancerVPCFactory.build();
5654

5755
describe('SummaryPanel', () => {
5856
beforeEach(() => {
@@ -79,7 +77,7 @@ describe('SummaryPanel', () => {
7977

8078
it('does not render anything if there is no nodebalancer', () => {
8179
queryMocks.useAllNodeBalancerConfigsQuery.mockReturnValue({
82-
data: undefined,
80+
data: null,
8381
});
8482
const { queryByText } = renderWithTheme(<SummaryPanel />);
8583

@@ -88,7 +86,7 @@ describe('SummaryPanel', () => {
8886

8987
it('does not render anything if there are no configs', () => {
9088
queryMocks.useNodeBalancerQuery.mockReturnValue({
91-
data: undefined,
89+
data: null,
9290
});
9391
const { queryByText } = renderWithTheme(<SummaryPanel />);
9492

@@ -104,14 +102,14 @@ describe('SummaryPanel', () => {
104102
expect(getByText(nodeBalancerDetails)).toBeVisible();
105103
expect(getByText('Ports:')).toBeVisible();
106104
expect(getByText('Backend Status:')).toBeVisible();
107-
expect(getByText('0 up, 2 down'));
105+
expect(getByText('0 up, 2 down')).toBeVisible();
108106
expect(getByText('Transferred:')).toBeVisible();
109107
expect(getByText('0 bytes')).toBeVisible();
110108
expect(getByText('Host Name:')).toBeVisible();
111109
expect(getByText('example.com')).toBeVisible();
112110
expect(getByText('Region:')).toBeVisible();
113-
// Type should not display for non-premium NBs
114-
expect(queryByText('Type:')).not.toBeInTheDocument();
111+
// Type should be visible and default to Basic since the NB is not premium
112+
expect(getByText('Basic')).toBeVisible();
115113
// Cluster should not display for if the NB is not associated with LKE or LKE-E
116114
expect(queryByText('Cluster:')).not.toBeInTheDocument();
117115

@@ -120,11 +118,11 @@ describe('SummaryPanel', () => {
120118
expect(getByText('mock-firewall-1')).toBeVisible();
121119

122120
// IP Address panel
123-
expect(getByText('IP Addresses')).toBeVisible();
121+
expect(getByText('Frontend Configuration')).toBeVisible();
124122
expect(getByText('0.0.0.0')).toBeVisible();
125123

126124
// VPC Details Panel
127-
expect(getByText('VPC')).toBeVisible();
125+
expect(getByText('Backend Configuration - VPC')).toBeVisible();
128126
expect(getByText('Subnets:')).toBeVisible();
129127
expect(getByText(`${nbVpcConfig.ipv4_range}`)).toBeVisible();
130128

@@ -133,19 +131,54 @@ describe('SummaryPanel', () => {
133131
expect(getByText('Add a tag')).toBeVisible();
134132
});
135133

134+
it('displays type: Basic if the nodebalancer is non premium', () => {
135+
queryMocks.useNodeBalancerQuery.mockReturnValue({
136+
data: nodeBalancerFactory.build({ type: 'common' }),
137+
});
138+
139+
const { getByText } = renderWithTheme(<SummaryPanel />);
140+
const typeElement = getByText((_, element) => {
141+
return (
142+
!!element?.hasAttribute('data-qa-type') &&
143+
element?.textContent === 'Type: Basic'
144+
);
145+
});
146+
expect(typeElement).toBeVisible();
147+
});
148+
136149
it('displays type: premium if the nodebalancer is premium', () => {
137150
queryMocks.useNodeBalancerQuery.mockReturnValue({
138151
data: nodeBalancerFactory.build({ type: 'premium' }),
139152
});
140153

141-
const { container } = renderWithTheme(<SummaryPanel />);
154+
const { getByText } = renderWithTheme(<SummaryPanel />);
142155

143-
expect(container.querySelector('[data-qa-type]')).toHaveTextContent(
144-
'Type: Premium'
145-
);
156+
const typeElement = getByText((_, element) => {
157+
return (
158+
!!element?.hasAttribute('data-qa-type') &&
159+
element?.textContent === 'Type: Premium'
160+
);
161+
});
162+
expect(typeElement).toBeVisible();
146163
});
147164

148-
it('displays link to cluster if it exists', () => {
165+
it('displays type: Enterprise if the nodebalancer is premium_40GB', () => {
166+
queryMocks.useNodeBalancerQuery.mockReturnValue({
167+
data: nodeBalancerFactory.build({ type: 'premium_40GB' }),
168+
});
169+
170+
const { getByText } = renderWithTheme(<SummaryPanel />);
171+
172+
const typeElement = getByText((_, element) => {
173+
return (
174+
!!element?.hasAttribute('data-qa-type') &&
175+
element?.textContent === 'Type: Enterprise'
176+
);
177+
});
178+
expect(typeElement).toBeVisible();
179+
});
180+
181+
it('displays link to cluster if it exists', async () => {
149182
queryMocks.useNodeBalancerQuery.mockReturnValue({
150183
data: nodeBalancerFactory.build({
151184
lke_cluster: {
@@ -157,11 +190,19 @@ describe('SummaryPanel', () => {
157190
}),
158191
});
159192

160-
const { container, getByText } = renderWithTheme(<SummaryPanel />);
193+
server.use(
194+
http.get('*/lke/clusters/:clusterId', () => {
195+
return HttpResponse.json({ id: 1, label: 'lke-123' });
196+
})
197+
);
198+
199+
const { getByText } = renderWithTheme(<SummaryPanel />);
161200

162201
expect(getByText('Cluster:')).toBeVisible();
163-
const clusterLink = container.querySelector('[data-qa-cluster] a');
164-
expect(clusterLink).toHaveTextContent('lke-123');
202+
const clusterLink = await waitFor(() => {
203+
return getByText('lke-123');
204+
});
205+
expect(clusterLink).toBeVisible();
165206
expect(clusterLink).toHaveAttribute(
166207
'href',
167208
'/kubernetes/clusters/1/summary'
@@ -186,16 +227,18 @@ describe('SummaryPanel', () => {
186227
})
187228
);
188229

189-
const { container } = renderWithTheme(<SummaryPanel />);
230+
const { getByText } = renderWithTheme(<SummaryPanel />);
190231

191-
await waitFor(() => {
192-
const clusterLink = container.querySelector('[data-qa-cluster]');
193-
expect(clusterLink).toHaveTextContent('Cluster: lke-123 (deleted)');
194-
expect(clusterLink).not.toHaveAttribute(
195-
'href',
196-
'/kubernetes/clusters/1/summary'
197-
);
232+
const clusterElement = await waitFor(() => {
233+
return getByText((_, element) => {
234+
return (
235+
!!element?.hasAttribute('data-qa-cluster') &&
236+
element?.textContent === 'Cluster: lke-123 (deleted)'
237+
);
238+
});
198239
});
240+
expect(clusterElement).toBeVisible();
241+
expect(clusterElement).not.toHaveAttribute('href');
199242
});
200243

201244
it('should disable "Add a tag" if user does not have permission', () => {

0 commit comments

Comments
 (0)