Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [M3-8855] - Surface Node Pool Tags #11368

Merged
merged 13 commits into from
Dec 17, 2024
5 changes: 5 additions & 0 deletions packages/api-v4/.changeset/pr-11368-added-1733420616390.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Added
---

Tags to `KubeNodePoolResponse` and `CreateNodePoolData` ([#11368](https://github.com/linode/manager/pull/11368))
2 changes: 2 additions & 0 deletions packages/api-v4/src/kubernetes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface KubeNodePoolResponse {
count: number;
id: number;
nodes: PoolNodeResponse[];
tags: string[];
type: string;
autoscaler: AutoscaleSettings;
disk_encryption?: EncryptionStatus; // @TODO LDE: remove optionality once LDE is fully rolled out
Expand All @@ -42,6 +43,7 @@ export interface CreateNodePoolData {
export interface UpdateNodePoolData {
autoscaler: AutoscaleSettings;
count: number;
tags: string[];
}

export interface AutoscaleSettings {
Expand Down
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-11368-added-1733415278919.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Added
---

Node Pool Tags to LKE Cluster details page ([#11368](https://github.com/linode/manager/pull/11368))
66 changes: 66 additions & 0 deletions packages/manager/cypress/e2e/core/kubernetes/lke-update.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,72 @@ describe('LKE cluster updates', () => {
});
});

it('can add and delete node pool tags', () => {
const mockCluster = kubernetesClusterFactory.build({
k8s_version: latestKubernetesVersion,
});

const mockNodePoolNoTags = nodePoolFactory.build({
id: 1,
type: 'g6-dedicated-4',
});

const mockNodePoolWithTags = {
...mockNodePoolNoTags,
tags: ['test-tag'],
};

mockGetCluster(mockCluster).as('getCluster');
mockGetClusterPools(mockCluster.id, [mockNodePoolNoTags]).as(
'getNodePoolsNoTags'
);
mockGetKubernetesVersions().as('getVersions');
mockUpdateNodePool(mockCluster.id, mockNodePoolWithTags).as('addTag');
mockGetDashboardUrl(mockCluster.id);
mockGetApiEndpoints(mockCluster.id);

cy.visitWithLogin(`/kubernetes/clusters/${mockCluster.id}`);
cy.wait(['@getCluster', '@getNodePoolsNoTags', '@getVersions']);

cy.get(`[data-qa-node-pool-id="${mockNodePoolNoTags.id}"]`).within(() => {
ui.button.findByTitle('Add a tag').should('be.visible').click();

cy.findByLabelText('Create or Select a Tag')
.should('be.visible')
.type(`${mockNodePoolWithTags.tags[0]}`);

ui.autocompletePopper
.findByTitle(`Create "${mockNodePoolWithTags.tags[0]}"`)
.scrollIntoView()
.should('be.visible')
.click();
});

mockGetClusterPools(mockCluster.id, [mockNodePoolWithTags]).as(
'getNodePoolsWithTags'
);

cy.wait(['@addTag', '@getNodePoolsWithTags']);

mockUpdateNodePool(mockCluster.id, mockNodePoolNoTags).as('deleteTag');
mockGetClusterPools(mockCluster.id, [mockNodePoolNoTags]).as(
'getNodePoolsNoTags'
);

// Delete the newly added node pool tag.
cy.get(`[data-qa-tag="${mockNodePoolWithTags.tags[0]}"]`)
.should('be.visible')
.within(() => {
cy.get('[data-qa-delete-tag="true"]').should('be.visible').click();
});

cy.wait(['@deleteTag', '@getNodePoolsNoTags']);

cy.get(`[data-qa-tag="${mockNodePoolWithTags.tags[0]}"]`).should(
'not.exist'
);
});

describe('LKE cluster updates for DC-specific prices', () => {
/*
* - Confirms node pool resize UI flow using mocked API responses.
Expand Down
1 change: 1 addition & 0 deletions packages/manager/src/factories/kubernetesCluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const nodePoolFactory = Factory.Sync.makeFactory<KubeNodePoolResponse>({
disk_encryption: 'enabled',
id: Factory.each((id) => id),
nodes: kubeLinodeFactory.buildList(3),
tags: [],
type: 'g6-standard-1',
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type { EncryptionStatus } from '@linode/api-v4/lib/linodes/types';

interface Props {
autoscaler: AutoscaleSettings;
clusterId: number;
encryptionStatus: EncryptionStatus | undefined;
handleClickResize: (poolId: number) => void;
isOnlyNodePool: boolean;
Expand All @@ -30,12 +31,14 @@ interface Props {
openRecycleAllNodesDialog: (poolId: number) => void;
openRecycleNodeDialog: (nodeID: string, linodeLabel: string) => void;
poolId: number;
tags: string[];
typeLabel: string;
}

export const NodePool = (props: Props) => {
const {
autoscaler,
clusterId,
encryptionStatus,
handleClickResize,
isOnlyNodePool,
Expand All @@ -45,6 +48,7 @@ export const NodePool = (props: Props) => {
openRecycleAllNodesDialog,
openRecycleNodeDialog,
poolId,
tags,
typeLabel,
} = props;

Expand Down Expand Up @@ -134,10 +138,12 @@ export const NodePool = (props: Props) => {
</Hidden>
</Paper>
<NodeTable
clusterId={clusterId}
encryptionStatus={encryptionStatus}
nodes={nodes}
openRecycleNodeDialog={openRecycleNodeDialog}
poolId={poolId}
tags={tags}
typeLabel={typeLabel}
/>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export const NodePoolsDisplay = (props: Props) => {
{poolsError && <ErrorState errorText={poolsError[0].reason} />}
<Stack spacing={2}>
{_pools?.map((thisPool) => {
const { disk_encryption, id, nodes } = thisPool;
const { disk_encryption, id, nodes, tags } = thisPool;

const thisPoolType = types?.find(
(thisType) => thisType.id === thisPool.type
Expand All @@ -131,12 +131,14 @@ export const NodePoolsDisplay = (props: Props) => {
setIsRecycleNodeOpen(true);
}}
autoscaler={thisPool.autoscaler}
clusterId={clusterID}
encryptionStatus={disk_encryption}
handleClickResize={handleOpenResizeDrawer}
isOnlyNodePool={pools?.length === 1}
key={id}
nodes={nodes ?? []}
poolId={thisPool.id}
tags={tags}
typeLabel={typeLabel}
/>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Typography } from '@linode/ui';
import { Box, Typography } from '@linode/ui';
import { styled } from '@mui/material/styles';

import VerticalDivider from 'src/assets/icons/divider-vertical.svg';
Expand All @@ -25,5 +25,38 @@ export const StyledVerticalDivider = styled(VerticalDivider, {
export const StyledTypography = styled(Typography, {
label: 'StyledTypography',
})(({ theme }) => ({
margin: `0 0 0 ${theme.spacing()}`,
margin: `0 ${theme.spacing(2)} 0 ${theme.spacing()}`,
[theme.breakpoints.down('md')]: {
padding: theme.spacing(),
},
}));

export const StyledNotEncryptedBox = styled(Box, {
label: 'StyledNotEncryptedBox',
})(({ theme }) => ({
alignItems: 'center',
display: 'flex',
margin: `0 ${theme.spacing(2)} 0 ${theme.spacing()}`,
}));

export const StyledPoolInfoBox = styled(Box, {
label: 'StyledPoolInfoBox',
})(() => ({
display: 'flex',
width: '50%',
}));

export const StyledTableFooter = styled(Box, {
label: 'StyledTableFooter',
})(({ theme }) => ({
alignItems: 'center',
background: theme.bg.bgPaper,
display: 'flex',
justifyContent: 'space-between',
padding: `0 ${theme.spacing(2)}`,
[theme.breakpoints.down('md')]: {
display: 'block',
flexDirection: 'column',
paddingBottom: theme.spacing(),
},
}));
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ import { kubeLinodeFactory } from 'src/factories/kubernetesCluster';
import { linodeFactory } from 'src/factories/linodes';
import { renderWithTheme } from 'src/utilities/testHelpers';

import { NodeTable, Props, encryptionStatusTestId } from './NodeTable';
import { NodeTable, encryptionStatusTestId } from './NodeTable';

import type { Props } from './NodeTable';

const mockLinodes = linodeFactory.buildList(3);

const mockKubeNodes = kubeLinodeFactory.buildList(3);

const props: Props = {
clusterId: 1,
encryptionStatus: 'enabled',
nodes: mockKubeNodes,
openRecycleNodeDialog: vi.fn(),
poolId: 1,
tags: [],
typeLabel: 'Linode 2G',
};

Expand Down
Loading
Loading