Skip to content
Merged
5 changes: 5 additions & 0 deletions packages/api-v4/.changeset/pr-13504-added-1775602581034.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Added
---

SubnetAssignedDatabaseData interface and update to Subnet to include databases property ([#13504](https://github.com/linode/manager/pull/13504))
7 changes: 7 additions & 0 deletions packages/api-v4/src/vpcs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export interface CreateSubnetPayload {

export interface Subnet extends CreateSubnetPayload {
created: string;
databases: SubnetAssignedDatabaseData[];
id: number;
linodes: SubnetAssignedLinodeData[];
nodebalancers: SubnetAssignedNodeBalancerData[];
Expand All @@ -68,6 +69,12 @@ export interface SubnetAssignedNodeBalancerData {
ipv4_range: string;
}

export interface SubnetAssignedDatabaseData {
id: number;
ipv4_range: string;
ipv6_ranges: null | { range: string }[];
}

export interface VPCIP {
active: boolean;
address: null | string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

DBaaS resource counts and databases resource table in VPC UI ([#13504](https://github.com/linode/manager/pull/13504))
1 change: 1 addition & 0 deletions packages/manager/src/dev-tools/FeatureFlagTool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const options: { flag: keyof Flags; label: string }[] = [
label: 'Object Storage Contextual Metrics',
},
{ flag: 'objSummaryPage', label: 'OBJ Summary Page' },
{ flag: 'vpcDbaasResources', label: 'VPC DBaaS Resources' },
{ flag: 'vpcIpv6', label: 'VPC IPv6' },
{ flag: 'reserveIp', label: 'Reserve IP' },
{ flag: 'marketplaceV2GlobalBanner', label: 'Marketplace V2 Global Banner' },
Expand Down
4 changes: 4 additions & 0 deletions packages/manager/src/factories/databases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ export const databaseInstanceFactory =
label: Factory.each((i) => `example.com-database-${i}`),
members: {
'2.2.2.2': 'primary',
'2.2.2.3': 'failover',
'2.2.2.4': 'failover',
},
platform: 'rdbms-default',
region: Factory.each((i) => possibleRegions[i % possibleRegions.length]),
Expand Down Expand Up @@ -268,6 +270,8 @@ export const databaseFactory = Factory.Sync.makeFactory<Database>({
label: Factory.each((i) => `database-${i}`),
members: {
'2.2.2.2': 'primary',
'2.2.2.3': 'failover',
'2.2.2.4': 'failover',
},
oldest_restore_time: '2024-09-15T17:15:12',
platform: 'rdbms-default',
Expand Down
25 changes: 25 additions & 0 deletions packages/manager/src/factories/subnets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Factory } from '@linode/utilities';

import type {
Subnet,
SubnetAssignedDatabaseData,
SubnetAssignedLinodeData,
SubnetAssignedNodeBalancerData,
} from '@linode/api-v4/lib/vpcs/types';
Expand All @@ -27,6 +28,23 @@ export const subnetAssignedNodebalancerDataFactory =
ipv4_range: Factory.each((i) => `192.168.${i}.0/30`),
});

export const subnetAssignedDatabaseDataFactory =
Factory.Sync.makeFactory<SubnetAssignedDatabaseData>({
id: Factory.each((i) => i),
ipv4_range: Factory.each((i) => `192.168.${i}.0/30`),
ipv6_ranges: Factory.each((i) => [
{
range: `2600:3c11:e41c:${i}::/64`,
},
{
range: `2600:3c11:e41c:${i}::/64`,
},
{
range: `2600:3c11:e41c:${i}::/64`,
},
]),
});

export const subnetFactory = Factory.Sync.makeFactory<Subnet>({
created: '2023-07-12T16:08:53',
id: Factory.each((i) => i),
Expand All @@ -46,5 +64,12 @@ export const subnetFactory = Factory.Sync.makeFactory<Subnet>({
})
)
),
databases: Factory.each((i) =>
Array.from({ length: 3 }, (_, arrIdx) =>
subnetAssignedDatabaseDataFactory.build({
id: i * 10 + arrIdx,
})
)
),
updated: '2023-07-12T16:08:53',
});
1 change: 1 addition & 0 deletions packages/manager/src/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ export interface Flags {
udp: boolean;
vmHostMaintenance: VMHostMaintenanceFlag;
volumeSummaryPage: boolean;
vpcDbaasResources: boolean;
vpcIpv6: boolean;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@
page_size: newDatabasesPagination.pageSize,
},
databasesFilter,
isDefaultEnabled // TODO (UIE-8634): Determine if check if still necessary
isDefaultEnabled, // TODO (UIE-8634): Determine if check is still necessary

Check warning on line 71 in packages/manager/src/features/Databases/DatabaseLanding/DatabaseLanding.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Complete the task associated to this "TODO" comment. Raw Output: {"ruleId":"sonarjs/todo-tag","severity":1,"message":"Complete the task associated to this \"TODO\" comment.","line":71,"column":26,"nodeType":null,"messageId":"completeTODO","endLine":71,"endColumn":30}
20000
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For more info, refer to my previous comment on the databases query.

);

if (databasesError) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import { Link } from 'src/components/Link';
import { DatabaseStatusDisplay } from 'src/features/Databases/DatabaseDetail/DatabaseStatusDisplay';
import { DatabaseEngineVersion } from 'src/features/Databases/DatabaseEngineVersion';
import { DatabaseActionMenu } from 'src/features/Databases/DatabaseLanding/DatabaseActionMenu';
import { useIsDatabasesEnabled } from 'src/features/Databases/utilities';
import {
getIsLinkInactive,
useIsDatabasesEnabled,
} from 'src/features/Databases/utilities';
import { isWithinDays, parseAPIDate } from 'src/utilities/date';
import { formatDate } from 'src/utilities/formatDate';

Expand Down Expand Up @@ -64,11 +67,6 @@ export const DatabaseRow = ({
const plan = types?.find((t: DatabaseType) => t.id === type);
const formattedPlan = plan && formatStorageUnits(plan.label);
const actualRegion = regions?.find((r) => r.id === region);
const isLinkInactive =
status === 'suspended' ||
status === 'suspending' ||
status === 'resuming' ||
status === 'migrated';
const { isDatabasesV2GA } = useIsDatabasesEnabled();

const configuration =
Expand Down Expand Up @@ -97,7 +95,7 @@ export const DatabaseRow = ({
flex: '0 1 20.5%',
}}
>
{isDatabasesV2GA && isLinkInactive ? (
{isDatabasesV2GA && getIsLinkInactive(status) ? (
label
) : (
<Link to={`/databases/${engine}/${id}`}>{label}</Link>
Expand Down
4 changes: 4 additions & 0 deletions packages/manager/src/features/Databases/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
DatabaseEngine,
DatabaseFork,
DatabaseInstance,
DatabaseStatus,
Engine,
PendingUpdates,
} from '@linode/api-v4';
Expand Down Expand Up @@ -256,3 +257,6 @@ export const convertPrivateToPublicHostname = (host: string) => {
const baseHostName = host.slice(privateStrIndex + 1);
return `public-${baseHostName}`;
};

export const getIsLinkInactive = (status: DatabaseStatus) =>
['migrated', 'resuming', 'suspended', 'suspending'].includes(status);
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const props = {
label: 'subnet-1',
linodes: [],
nodebalancers: [],
databases: [],
created: '',
updated: '',
} as Subnet,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import * as React from 'react';

import {
databaseInstanceFactory,
subnetAssignedDatabaseDataFactory,
} from 'src/factories';
import { renderWithTheme, wrapWithTableBody } from 'src/utilities/testHelpers';

import { SubnetDatabaseRow } from './SubnetDatabaseRow';

import type { DatabaseInstance } from '@linode/api-v4';

const mockIpv6Range = '0000:db1::/32';
const databaseLabel = 'test-database-1';
const mockDatabase = databaseInstanceFactory.build({
id: 1,
label: databaseLabel,
});

const mockAssignedDatabase = subnetAssignedDatabaseDataFactory.build({
id: 1,
ipv4_range: '1.1.1.1/32',
ipv6_ranges: [{ range: mockIpv6Range }],
});

describe('SubnetDatabaseRow', () => {
beforeEach(() => {
vi.resetAllMocks();
});

it('should render SubnetDatabaseRow', () => {
const dbWithPrimary: DatabaseInstance = {
...mockDatabase,
members: { '2.2.2.2': 'primary' },
};

const { getByText } = renderWithTheme(
wrapWithTableBody(
<SubnetDatabaseRow
assignedDatabase={mockAssignedDatabase}
database={dbWithPrimary}
/>
)
);
getByText(databaseLabel);
getByText(mockAssignedDatabase.ipv4_range);
getByText(mockIpv6Range);
getByText('2.2.2.2');
});

it('should render SubnetDatabaseRow with multiple failover IPs', () => {
const dbWithFailovers: DatabaseInstance = {
...mockDatabase,
members: {
'2.2.2.2': 'primary',
'2.2.2.3': 'failover',
'2.2.2.4': 'failover',
},
};

const { getByText } = renderWithTheme(
wrapWithTableBody(
<SubnetDatabaseRow
assignedDatabase={mockAssignedDatabase}
database={dbWithFailovers}
/>
)
);
getByText(databaseLabel);
getByText(mockAssignedDatabase.ipv4_range);
getByText(mockIpv6Range);
getByText('2.2.2.2, 2.2.2.3, 2.2.2.4');
});

it('should render SubnetDatabaseRow with no members', () => {
const dbWithNoMembers = {
...mockDatabase,
members: {},
};

const { getByText } = renderWithTheme(
wrapWithTableBody(
<SubnetDatabaseRow
assignedDatabase={mockAssignedDatabase}
database={dbWithNoMembers}
/>
)
);
getByText(databaseLabel);
getByText(mockAssignedDatabase.ipv4_range);
getByText(mockIpv6Range);
getByText('β€”');
});

it('should render SubnetDatabaseRow with no ipv6 ranges', () => {
const assignedDatabaseWithNoIpv6 = {
...mockAssignedDatabase,
ipv6_ranges: null,
};

const { getByText } = renderWithTheme(
wrapWithTableBody(
<SubnetDatabaseRow
assignedDatabase={assignedDatabaseWithNoIpv6}
database={mockDatabase}
/>
)
);
getByText(databaseLabel);
getByText(mockAssignedDatabase.ipv4_range);
getByText('β€”');
});

it('should render SubnetDatabaseRow with HA cluster', () => {
const haDatabase = databaseInstanceFactory.build({
id: 1,
label: databaseLabel,
cluster_size: 3,
});

const { getByText } = renderWithTheme(
wrapWithTableBody(
<SubnetDatabaseRow
assignedDatabase={mockAssignedDatabase}
database={haDatabase}
/>
)
);
getByText(databaseLabel);
getByText('HA');
});
});
Loading
Loading