Skip to content

Commit

Permalink
feat: [M3-8855] - Surface Node Pool Tags (linode#11368)
Browse files Browse the repository at this point in the history
* surface tags for node pools

* wip e2e

* fix e2e

* Added changeset: Surface Node Pool Tags

* fix unit test

* Added changeset: tags to KubeNodePoolResponse and CreateNodePoolData

* clean up

* address feedback

* clean up

* fix tags cutting off in mobile

* spacing

* use md breakpoint instead of sm

* styling
  • Loading branch information
hana-akamai authored and dmcintyr-akamai committed Jan 9, 2025
1 parent eb48313 commit d6f0b7e
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 37 deletions.
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 @@ -1063,6 +1063,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 @@ -30,6 +30,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

0 comments on commit d6f0b7e

Please sign in to comment.