Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-13347-changed-1769758380402.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Changed
---

UIE-10060 : Support new GPU v3 RTX Pro 6000 Blackwell plans in Kubernetes for both LKE and LKE-E ([#13347](https://github.com/linode/manager/pull/13347))
137 changes: 40 additions & 97 deletions packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1469,8 +1469,6 @@
validEnterprisePlanTabs.forEach((tab) => {
ui.tabList.findTabByTitle(tab).should('be.visible');
});
// Confirm the GPU tab is not visible in the plans panel for LKE-E.
ui.tabList.findTabByTitle('GPU').should('not.exist');

// Add a node pool for each selected plan, and confirm that the
// selected node pool plan is added to the checkout bar.
Expand Down Expand Up @@ -1910,106 +1908,51 @@
mockGetRegionAvailability(mockRegion.id, mockRegionAvailability).as(
'getRegionAvailability'
);
mockGetTieredKubernetesVersions('enterprise', [
latestEnterpriseTierKubernetesVersion,
]).as('getEnterpriseTieredVersions');
});

describe('standard tier', () => {
it('enabled feature flag includes blackwells', () => {
mockAppendFeatureFlags({
kubernetesBlackwellPlans: true,
}).as('getFeatureFlags');
cy.visitWithLogin('/kubernetes/create');
cy.wait(['@getFeatureFlags', '@getRegions', '@getLinodeTypes']);

ui.regionSelect.find().click();
ui.regionSelect.find().clear();
ui.regionSelect.find().type(`${mockRegion.label}{enter}`);
cy.wait('@getRegionAvailability');
// Navigate to "GPU" tab
ui.tabList.findTabByTitle('GPU').scrollIntoView();
ui.tabList.findTabByTitle('GPU').should('be.visible').click();

cy.findByRole('table', {
name: 'List of Linode Plans',
}).within(() => {
cy.get('tbody tr')
.should('have.length', 4)
.each((row, index) => {
cy.wrap(row).within(() => {
cy.get('td')
.eq(0)
.within(() => {
cy.findByText(mockBlackwellLinodeTypes[index].label).should(
'be.visible'
);
});
ui.button
.findByTitle('Configure Pool')
.should('be.visible')
.should('be.enabled');
});
});
});
});

it('disabled feature flag excludes blackwells', () => {
mockAppendFeatureFlags({
kubernetesBlackwellPlans: false,
}).as('getFeatureFlags');

cy.visitWithLogin('/kubernetes/create');
cy.wait(['@getFeatureFlags', '@getRegions', '@getLinodeTypes']);

ui.regionSelect.find().click();
ui.regionSelect.find().clear();
ui.regionSelect.find().type(`${mockRegion.label}{enter}`);
cy.wait('@getRegionAvailability');
// Navigate to "GPU" tab
// "GPU" tab hidden
ui.tabList.findTabByTitle('GPU').should('not.exist');
});
});
describe('enterprise tier hides GPU tab', () => {
beforeEach(() => {
// necessary to prevent crash after selecting Enterprise button
mockGetTieredKubernetesVersions('enterprise', [
latestEnterpriseTierKubernetesVersion,
]).as('getEnterpriseTieredVersions');
});
it('enabled feature flag', () => {
mockAppendFeatureFlags({
kubernetesBlackwellPlans: true,
}).as('getFeatureFlags');
it('both tiers should include blackwell GPUs', () => {
cy.visitWithLogin('/kubernetes/create');
cy.wait(['@getRegions', '@getLinodeTypes']);

cy.visitWithLogin('/kubernetes/create');
cy.wait(['@getFeatureFlags', '@getRegions', '@getLinodeTypes']);

cy.findByText('LKE Enterprise').click();
cy.wait(['@getEnterpriseTieredVersions']);
ui.regionSelect.find().click();
ui.regionSelect.find().clear();
ui.regionSelect.find().type(`${mockRegion.label}{enter}`);
cy.wait('@getRegionAvailability');
// "GPU" tab hidden
ui.tabList.findTabByTitle('GPU').should('not.exist');
ui.regionSelect.find().click();
ui.regionSelect.find().clear();
ui.regionSelect.find().type(`${mockRegion.label}{enter}`);
cy.wait('@getRegionAvailability');
// Navigate to "GPU" tab
ui.tabList.findTabByTitle('GPU').scrollIntoView();
ui.tabList.findTabByTitle('GPU').should('be.visible').click();

cy.findByRole('table', {
name: 'List of Linode Plans',
}).within(() => {
cy.get('tbody tr')
.should('have.length', 4)
.each((row, index) => {
cy.wrap(row).within(() => {

Check warning on line 1934 in packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Refactor this code to not nest functions more than 4 levels deep. Raw Output: {"ruleId":"sonarjs/no-nested-functions","severity":1,"message":"Refactor this code to not nest functions more than 4 levels deep.","line":1934,"column":34,"nodeType":null,"endLine":1934,"endColumn":36}
cy.get('td')
.eq(0)
.within(() => {
cy.findByText(mockBlackwellLinodeTypes[index].label).should(
'be.visible'
);
});
ui.button
.findByTitle('Configure Pool')
.should('be.visible')
.should('be.enabled');
});
});
});

it('disabled feature flag', () => {
mockAppendFeatureFlags({
kubernetesBlackwellPlans: false,
}).as('getFeatureFlags');

cy.visitWithLogin('/kubernetes/create');
cy.wait(['@getFeatureFlags', '@getRegions', '@getLinodeTypes']);

ui.regionSelect.find().click();
ui.regionSelect.find().clear();
ui.regionSelect.find().type(`${mockRegion.label}{enter}`);
cy.findByText('LKE Enterprise').click();
cy.wait(['@getEnterpriseTieredVersions']);
2;
// "GPU" tab hidden
ui.tabList.findTabByTitle('GPU').should('not.exist');
});
cy.findByText('LKE Enterprise').click();
cy.wait(['@getEnterpriseTieredVersions']);
ui.regionSelect.find().click();
ui.regionSelect.find().clear();
ui.regionSelect.find().type(`${mockRegion.label}{enter}`);
ui.tabList.findTabByTitle('GPU').should('exist');
});
});

Expand Down
1 change: 0 additions & 1 deletion packages/manager/src/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ export interface Flags {
iamDelegation: BaseFeatureFlag;
iamLimitedAvailabilityBadges: boolean;
ipv6Sharing: boolean;
kubernetesBlackwellPlans: boolean;
limitsEvolution: LimitsEvolution;
linodeCloneFirewall: boolean;
linodeDiskEncryption: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,7 @@ export const KubernetesPlansPanel = (props: Props) => {
}

return (
!type.id.includes('dedicated-edge') &&
!type.id.includes('nanode-edge') &&
// Filter out GPU types for enterprise; otherwise, return the rest of the types.
// TODO: remove this once GPU plans are supported in LKE-E (Q3 2025)
(selectedTier === 'enterprise' ? !type.id.includes('gpu') : true) &&
// Filter out Blackwell plans for kubernetes (for now)
!(type.id.includes('blackwell') && !flags.kubernetesBlackwellPlans)
!type.id.includes('dedicated-edge') && !type.id.includes('nanode-edge')
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ const ALL_GPU_OPTIONS: SelectOption<PlanFilterGPU>[] = [
];

interface GPUPlanFilterComponentProps {
disabled?: boolean;
onResult: (result: PlanFilterRenderResult) => void;
plans: PlanWithAvailability[];
resetPagination: () => void;
}

const GPUPlanFilterComponent = React.memo(
(props: GPUPlanFilterComponentProps) => {
const { onResult, plans, resetPagination } = props;
const { disabled = false, onResult, plans, resetPagination } = props;

// Local state - persists automatically because component stays mounted
const [gpuType, setGpuType] =
Expand Down Expand Up @@ -112,6 +113,7 @@ const GPUPlanFilterComponent = React.memo(
<Select
aria-labelledby="plan-filter-gpu-label"
data-testid="plan-filter-gpu"
disabled={disabled}
id="plan-filter-gpu"
label="GPU Plans"
onChange={handleGpuTypeChange}
Expand All @@ -129,6 +131,7 @@ const GPUPlanFilterComponent = React.memo(
};
}, [
GPU_OPTIONS_BASED_ON_AVAILABLE_PLANS,
disabled,
filteredPlans,
gpuType,
handleGpuTypeChange,
Expand All @@ -152,8 +155,10 @@ export const createGPUPlanFilterRenderProp = () => {
onResult,
plans,
resetPagination,
shouldDisableFilters = false,
}: PlanFilterRenderArgs): React.ReactNode => (
<GPUPlanFilterComponent
disabled={shouldDisableFilters}
onResult={onResult}
plans={plans}
resetPagination={resetPagination}
Expand Down