Skip to content

Commit 3ec53b7

Browse files
mjac0bscpathipa
andauthored
test: [M3-10464] - Add Cypress test confirming LKE-E cluster details page shows VPC details (linode#12700)
* Update types to include null * Add test coverage for VPC in LKE details summary * Add test coverage for VPC IP columns; improve comments * Add test coverage for case 2, standard cluster * Added changeset: Add `lke-enterprise-read` and `lke-standard-read` Cypress specs; test LKE-E VPC coverage * Added changeset: Update `KubernetesCluster` type to include `null` * Improve changeset * Mock the account capability for LKE-E to fix test failures --------- Co-authored-by: cpathipa <119517080+cpathipa@users.noreply.github.com>
1 parent 8af48ab commit 3ec53b7

File tree

6 files changed

+368
-3
lines changed

6 files changed

+368
-3
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": Upcoming Features
3+
---
4+
5+
Update `KubernetesCluster` `vpc_id` and `subnet_id` types to include `null` ([#12700](https://github.com/linode/manager/pull/12700))

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export interface KubernetesCluster {
3939
* Upcoming Feature Notice - LKE-E:** this property may not be available to all customers
4040
* and may change in subsequent releases.
4141
*/
42-
subnet_id?: number;
42+
subnet_id?: null | number;
4343
tags: string[];
4444
/** Marked as 'optional' in this existing interface to prevent duplicated code for beta functionality, in line with the apl_enabled approach.
4545
* @todo LKE-E - Make this field required once LKE-E is in GA. tier defaults to 'standard' in the API.
@@ -50,7 +50,7 @@ export interface KubernetesCluster {
5050
* Upcoming Feature Notice - LKE-E:** this property may not be available to all customers
5151
* and may change in subsequent releases.
5252
*/
53-
vpc_id?: number;
53+
vpc_id?: null | number;
5454
}
5555

5656
export interface KubeNodePoolResponse {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Tests
3+
---
4+
5+
Add `lke-enterprise-read` and `lke-standard-read` Cypress specs; test LKE-E VPC coverage ([#12700](https://github.com/linode/manager/pull/12700))
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
/**
2+
* Confirms read operations on LKE-Enterprise clusters.
3+
*/
4+
5+
import {
6+
linodeFactory,
7+
linodeIPFactory,
8+
profileFactory,
9+
} from '@linode/utilities';
10+
import { mockGetAccount } from 'support/intercepts/account';
11+
import { mockAppendFeatureFlags } from 'support/intercepts/feature-flags';
12+
import {
13+
mockGetLinodeIPAddresses,
14+
mockGetLinodes,
15+
} from 'support/intercepts/linodes';
16+
import {
17+
mockGetCluster,
18+
mockGetClusterPools,
19+
mockGetKubernetesVersions,
20+
} from 'support/intercepts/lke';
21+
import { mockGetProfile } from 'support/intercepts/profile';
22+
import { mockGetVPC } from 'support/intercepts/vpc';
23+
24+
import {
25+
accountFactory,
26+
kubeLinodeFactory,
27+
kubernetesClusterFactory,
28+
nodePoolFactory,
29+
subnetFactory,
30+
vpcFactory,
31+
} from 'src/factories';
32+
33+
const mockProfile = profileFactory.build();
34+
35+
const mockVPC = vpcFactory.build({
36+
id: 123,
37+
label: 'lke-e-vpc',
38+
subnets: [subnetFactory.build()],
39+
});
40+
41+
const mockClusterWithVPC = kubernetesClusterFactory.build({
42+
id: 1,
43+
vpc_id: mockVPC.id,
44+
subnet_id: mockVPC.subnets[0].id,
45+
tier: 'enterprise',
46+
});
47+
const mockClusterWithoutVPC = kubernetesClusterFactory.build({
48+
id: 2,
49+
vpc_id: null,
50+
tier: 'enterprise',
51+
});
52+
const mockNodePools = [
53+
nodePoolFactory.build({
54+
id: 1,
55+
nodes: [kubeLinodeFactory.build()],
56+
count: 1,
57+
}),
58+
];
59+
60+
const mockLinodes = mockNodePools.map((pool, i) =>
61+
linodeFactory.build({
62+
id: pool.nodes[i].instance_id ?? undefined,
63+
lke_cluster_id: mockClusterWithVPC.id,
64+
type: pool.type,
65+
})
66+
);
67+
const mockLinodeIPs = linodeIPFactory.build({
68+
ipv4: {
69+
public: [
70+
{
71+
address: '192.0.2.1',
72+
linode_id: mockLinodes[0].id,
73+
},
74+
],
75+
private: [
76+
{
77+
linode_id: mockLinodes[0].id,
78+
},
79+
],
80+
vpc: [
81+
{
82+
address: '10.0.0.1',
83+
linode_id: mockLinodes[0].id,
84+
vpc_id: mockVPC.id,
85+
subnet_id: mockVPC.subnets[0].id,
86+
},
87+
],
88+
},
89+
ipv6: {
90+
slaac: {
91+
address: '2600:abcd::efgh:ijkl:mnop:qrst',
92+
linode_id: mockLinodes[0].id,
93+
},
94+
link_local: {
95+
linode_id: mockLinodes[0].id,
96+
},
97+
vpc: [
98+
{
99+
linode_id: mockLinodes[0].id,
100+
vpc_id: mockVPC.id,
101+
subnet_id: mockVPC.subnets[0].id,
102+
ipv6_addresses: [
103+
{
104+
slaac_address: '2600:1234::abcd:5678:efgh:9012',
105+
},
106+
],
107+
},
108+
],
109+
},
110+
});
111+
112+
/**
113+
* Confirms the expected information is displayed in the cluster summary section of the cluster details page:
114+
* - Confirms the linked VPC is shown for an LKE-E cluster when it exists.
115+
* - Confirms a linked VPC is not shown for an LKE-E cluster when it doesn't exist.
116+
*/
117+
describe('LKE-E Cluster Summary - VPC Section', () => {
118+
beforeEach(() => {
119+
// TODO LKE-E: Remove once feature is in GA
120+
mockAppendFeatureFlags({
121+
lkeEnterprise: { enabled: true, la: true, phase2Mtc: true },
122+
});
123+
mockGetAccount(
124+
accountFactory.build({
125+
capabilities: ['Kubernetes Enterprise'],
126+
})
127+
).as('getAccount');
128+
});
129+
/*
130+
* Confirms LKE-E summary page shows VPC info and links to the correct VPC page when a vpc_id is present.
131+
*/
132+
it('shows linked VPC in summary for cluster with a VPC', () => {
133+
mockGetCluster(mockClusterWithVPC).as('getCluster');
134+
mockGetKubernetesVersions().as('getVersions');
135+
mockGetClusterPools(mockClusterWithVPC.id, []).as('getNodePools');
136+
mockGetVPC(mockVPC).as('getVPC');
137+
mockGetProfile(mockProfile).as('getProfile');
138+
139+
cy.visitWithLogin(`/kubernetes/clusters/${mockClusterWithVPC.id}/summary`);
140+
cy.wait([
141+
'@getCluster',
142+
'@getNodePools',
143+
'@getVersions',
144+
'@getVPC',
145+
'@getProfile',
146+
]);
147+
148+
// Verify VPC details appear in the summary
149+
cy.get('[data-qa-kube-entity-footer]').within(() => {
150+
cy.contains('VPC:').should('exist');
151+
cy.findByTestId('assigned-lke-cluster-label')
152+
.should('contain.text', mockVPC.label)
153+
.should('have.attr', 'href')
154+
.and('include', `/vpcs/${mockVPC.id}`);
155+
});
156+
157+
// Navigate to the VPC by clicking the link
158+
cy.findByTestId('assigned-lke-cluster-label').click();
159+
160+
// Verify the VPC details page loads
161+
cy.url().should('include', `/vpcs/${mockVPC.id}`);
162+
cy.contains(mockVPC.label).should('exist');
163+
});
164+
165+
/*
166+
* Confirms VPC info is not shown when cluster's vpc_id is null.
167+
*/
168+
it('does not show linked VPC in summary when cluster does not specify a VPC', () => {
169+
mockGetCluster(mockClusterWithoutVPC).as('getCluster');
170+
mockGetKubernetesVersions().as('getVersions');
171+
mockGetClusterPools(mockClusterWithoutVPC.id, []).as('getNodePools');
172+
mockGetProfile(mockProfile).as('getProfile');
173+
174+
cy.visitWithLogin(
175+
`/kubernetes/clusters/${mockClusterWithoutVPC.id}/summary`
176+
);
177+
cy.wait(['@getCluster', '@getNodePools', '@getVersions', '@getProfile']);
178+
179+
// Confirm that no VPC label or link is shown in the summary section
180+
cy.get('[data-qa-kube-entity-footer]').within(() => {
181+
cy.contains('VPC:').should('not.exist');
182+
cy.findByTestId('assigned-lke-cluster-label').should('not.exist');
183+
});
184+
});
185+
});
186+
187+
/**
188+
* Confirms the expected information is shown for a cluster's node pools on the cluster details page.
189+
*/
190+
describe('LKE-E Node Pools', () => {
191+
/**
192+
* - Confirms the VPC IP address table headers are shown in the node table.
193+
* - Confirms the IP address data is shown for a node in the node pool.
194+
*/
195+
it('shows VPC IPv4 and IPv6 columns for an LKE-E cluster', () => {
196+
mockAppendFeatureFlags({
197+
// TODO LKE-E: Remove once feature is in GA
198+
lkeEnterprise: { enabled: true, la: true, phase2Mtc: true },
199+
});
200+
mockGetAccount(
201+
accountFactory.build({
202+
capabilities: ['Kubernetes Enterprise'],
203+
})
204+
).as('getAccount');
205+
206+
mockGetCluster(mockClusterWithVPC).as('getCluster');
207+
mockGetKubernetesVersions().as('getVersions');
208+
mockGetClusterPools(mockClusterWithVPC.id, mockNodePools).as(
209+
'getNodePools'
210+
);
211+
mockGetVPC(mockVPC).as('getVPC');
212+
mockGetProfile(mockProfile).as('getProfile');
213+
mockGetLinodes(mockLinodes).as('getLinodes');
214+
mockGetLinodeIPAddresses(mockLinodes[0].id, mockLinodeIPs).as(
215+
'getLinodeIPs'
216+
);
217+
218+
cy.visitWithLogin(`/kubernetes/clusters/${mockClusterWithVPC.id}/summary`);
219+
cy.wait([
220+
'@getCluster',
221+
'@getNodePools',
222+
'@getVersions',
223+
'@getProfile',
224+
'@getVPC',
225+
]);
226+
227+
// Confirm VPC IP columns are present in the table header
228+
cy.get('[aria-label="List of Your Cluster Nodes"] thead').within(() => {
229+
cy.contains('th', 'VPC IPv4').should('be.visible');
230+
cy.contains('th', 'VPC IPv6').should('be.visible');
231+
});
232+
233+
// Confirm VPC IP addresses are present in the table data
234+
const vpcIPv6 =
235+
mockLinodeIPs.ipv6?.vpc?.[0]?.ipv6_addresses?.[0]?.slaac_address;
236+
const vpcIPv4 = mockLinodeIPs.ipv4?.vpc?.[0]?.address;
237+
238+
cy.get('[data-qa-node-row]').within(() => {
239+
cy.contains('td', vpcIPv6).should('be.visible');
240+
cy.contains('td', vpcIPv4).should('be.visible');
241+
});
242+
});
243+
});
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* Confirms read operations on LKE standard clusters.
3+
*/
4+
5+
import { linodeFactory, profileFactory } from '@linode/utilities';
6+
import { mockGetAccount } from 'support/intercepts/account';
7+
import { mockAppendFeatureFlags } from 'support/intercepts/feature-flags';
8+
import { mockGetLinodes } from 'support/intercepts/linodes';
9+
import {
10+
mockGetCluster,
11+
mockGetClusterPools,
12+
mockGetKubernetesVersions,
13+
} from 'support/intercepts/lke';
14+
import { mockGetProfile } from 'support/intercepts/profile';
15+
16+
import {
17+
accountFactory,
18+
kubeLinodeFactory,
19+
kubernetesClusterFactory,
20+
nodePoolFactory,
21+
} from 'src/factories';
22+
23+
const mockProfile = profileFactory.build();
24+
25+
const mockCluster = kubernetesClusterFactory.build({
26+
id: 3,
27+
tier: 'standard',
28+
vpc_id: undefined,
29+
subnet_id: undefined,
30+
});
31+
32+
const mockNodePools = [
33+
nodePoolFactory.build({
34+
id: 2,
35+
nodes: [kubeLinodeFactory.build()],
36+
count: 1,
37+
}),
38+
];
39+
40+
const mockLinodes = mockNodePools.map((pool, i) =>
41+
linodeFactory.build({
42+
id: pool.nodes[i].instance_id ?? undefined,
43+
lke_cluster_id: mockCluster.id,
44+
type: pool.type,
45+
})
46+
);
47+
48+
/**
49+
* Confirms the expected information is displayed in the cluster summary section of the cluster details page.
50+
*/
51+
describe('LKE Cluster Summary', () => {
52+
it('does not show linked VPC in summary for a standard cluster', () => {
53+
// TODO LKE-E: Remove once feature is in GA
54+
mockAppendFeatureFlags({
55+
lkeEnterprise: { enabled: true, la: true, phase2Mtc: true },
56+
});
57+
mockGetAccount(
58+
accountFactory.build({
59+
capabilities: ['Kubernetes Enterprise'],
60+
})
61+
).as('getAccount');
62+
63+
mockGetCluster(mockCluster).as('getCluster');
64+
mockGetKubernetesVersions().as('getVersions');
65+
mockGetClusterPools(mockCluster.id, []).as('getNodePools');
66+
mockGetProfile(mockProfile).as('getProfile');
67+
68+
cy.visitWithLogin(`/kubernetes/clusters/${mockCluster.id}/summary`);
69+
cy.wait(['@getCluster', '@getNodePools', '@getVersions', '@getProfile']);
70+
71+
// Confirm that no VPC label or link is shown in the summary section
72+
cy.get('[data-qa-kube-entity-footer]').within(() => {
73+
cy.contains('VPC:').should('not.exist');
74+
cy.findByTestId('assigned-lke-cluster-label').should('not.exist');
75+
});
76+
});
77+
});
78+
79+
/**
80+
* Confirms the expected information is shown for a cluster's node pools on the cluster details page.
81+
*/
82+
describe('LKE Node Pools', () => {
83+
/**
84+
* Confirms standard LKE clusters do not show VPC IP columns when the LKE-Enterprise Phase 2 feature flag is enabled.
85+
*/
86+
it('does not show VPC IP columns for standard LKE cluster', () => {
87+
// TODO LKE-E: Remove once feature is in GA
88+
mockAppendFeatureFlags({
89+
lkeEnterprise: { enabled: true, la: true, phase2Mtc: true },
90+
});
91+
mockGetAccount(
92+
accountFactory.build({
93+
capabilities: ['Kubernetes Enterprise'],
94+
})
95+
).as('getAccount');
96+
97+
mockGetCluster(mockCluster).as('getCluster');
98+
mockGetClusterPools(mockCluster.id, mockNodePools).as('getNodePools');
99+
mockGetKubernetesVersions().as('getVersions');
100+
mockGetProfile(profileFactory.build()).as('getProfile');
101+
mockGetLinodes(mockLinodes).as('getLinodes');
102+
103+
cy.visitWithLogin(`/kubernetes/clusters/${mockCluster.id}/summary`);
104+
cy.wait(['@getCluster', '@getNodePools', '@getVersions', '@getProfile']);
105+
106+
// Confirm VPC IP columns are not present in the node table
107+
cy.get('[aria-label="List of Your Cluster Nodes"] thead').within(() => {
108+
cy.contains('th', 'VPC IPv4').should('not.exist');
109+
cy.contains('th', 'VPC IPv6').should('not.exist');
110+
});
111+
});
112+
});

packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeEntityDetailFooter.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ interface FooterProps {
3434
isLoadingKubernetesACL: boolean;
3535
setControlPlaneACLDrawerOpen: React.Dispatch<React.SetStateAction<boolean>>;
3636
sx?: SxProps;
37-
vpcId: number | undefined;
37+
vpcId: null | number | undefined;
3838
}
3939

4040
export const KubeEntityDetailFooter = React.memo((props: FooterProps) => {

0 commit comments

Comments
 (0)