diff --git a/packages/api-v4/.changeset/pr-10786-added-1723662117687.md b/packages/api-v4/.changeset/pr-10786-added-1723662117687.md new file mode 100644 index 00000000000..960ca5330b4 --- /dev/null +++ b/packages/api-v4/.changeset/pr-10786-added-1723662117687.md @@ -0,0 +1,5 @@ +--- +"@linode/api-v4": Added +--- + +Managed Databases V2` capability and types ([#10786](https://github.com/linode/manager/pull/10786)) diff --git a/packages/api-v4/src/account/types.ts b/packages/api-v4/src/account/types.ts index dad426d63d7..4e266b064e0 100644 --- a/packages/api-v4/src/account/types.ts +++ b/packages/api-v4/src/account/types.ts @@ -72,6 +72,7 @@ export type AccountCapability = | 'LKE HA Control Planes' | 'Machine Images' | 'Managed Databases' + | 'Managed Databases V2' | 'NodeBalancers' | 'Object Storage Access Key Regions' | 'Object Storage Endpoint Types' diff --git a/packages/api-v4/src/databases/types.ts b/packages/api-v4/src/databases/types.ts index 71c526be374..3221a8c61b6 100644 --- a/packages/api-v4/src/databases/types.ts +++ b/packages/api-v4/src/databases/types.ts @@ -81,9 +81,11 @@ export interface DatabaseInstance { * A key/value object where the key is an IP address and the value is a member type. */ members: Record; + platform?: string; } -export type ClusterSize = 1 | 3; +export type ClusterSize = 1 | 2 | 3; + type ReadonlyCount = 0 | 2; export type MySQLReplicationType = 'none' | 'semi_synch' | 'asynch'; diff --git a/packages/api-v4/src/regions/types.ts b/packages/api-v4/src/regions/types.ts index cb159254e95..c17934076bb 100644 --- a/packages/api-v4/src/regions/types.ts +++ b/packages/api-v4/src/regions/types.ts @@ -12,6 +12,7 @@ export type Capabilities = | 'Kubernetes' | 'Linodes' | 'Managed Databases' + | 'Managed Databases V2' | 'Metadata' | 'NodeBalancers' | 'Object Storage' diff --git a/packages/manager/.changeset/pr-10786-upcoming-features-1723662207016.md b/packages/manager/.changeset/pr-10786-upcoming-features-1723662207016.md new file mode 100644 index 00000000000..60c3e568960 --- /dev/null +++ b/packages/manager/.changeset/pr-10786-upcoming-features-1723662207016.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Update DBaaS menu item with V1 or V2 capability, add mock data ([#10786](https://github.com/linode/manager/pull/10786)) diff --git a/packages/manager/src/__data__/regionsData.ts b/packages/manager/src/__data__/regionsData.ts index 0a3ab6eaf2e..836fbcafea7 100644 --- a/packages/manager/src/__data__/regionsData.ts +++ b/packages/manager/src/__data__/regionsData.ts @@ -98,6 +98,7 @@ export const regions: Region[] = [ 'Vlans', 'VPCs', 'Managed Databases', + 'Managed Databases V2', 'Metadata', 'Premium Plans', 'Placement Group', @@ -129,6 +130,7 @@ export const regions: Region[] = [ 'Vlans', 'VPCs', 'Managed Databases', + 'Managed Databases V2', 'Metadata', 'Premium Plans', ], @@ -484,6 +486,7 @@ export const regions: Region[] = [ 'VPCs', 'Block Storage Migrations', 'Managed Databases', + 'Managed Databases V2', 'Placement Group', ], country: 'us', @@ -511,6 +514,7 @@ export const regions: Region[] = [ 'Cloud Firewall', 'Block Storage Migrations', 'Managed Databases', + 'Managed Databases V2', 'Placement Group', ], country: 'us', @@ -542,6 +546,7 @@ export const regions: Region[] = [ 'VPCs', 'Block Storage Migrations', 'Managed Databases', + 'Managed Databases V2', 'Placement Group', ], country: 'us', diff --git a/packages/manager/src/assets/icons/db-logo.svg b/packages/manager/src/assets/icons/db-logo.svg new file mode 100644 index 00000000000..5776006d284 --- /dev/null +++ b/packages/manager/src/assets/icons/db-logo.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx index 65233b9bed3..e28aca7b644 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx @@ -187,7 +187,7 @@ export const PrimaryNav = (props: PrimaryNavProps) => { hide: !isDatabasesEnabled, href: '/databases', icon: , - isBeta: flags.databaseBeta, + isBeta: flags.dbaasV2?.beta, }, { activeLinks: ['/kubernetes/create'], @@ -249,7 +249,7 @@ export const PrimaryNav = (props: PrimaryNavProps) => { isDatabasesEnabled, isManaged, allowMarketplacePrefetch, - flags.databaseBeta, + flags.dbaasV2, isPlacementGroupsEnabled, flags.placementGroups, isACLPEnabled, diff --git a/packages/manager/src/dev-tools/FeatureFlagTool.tsx b/packages/manager/src/dev-tools/FeatureFlagTool.tsx index 3de0659ba92..67de298235c 100644 --- a/packages/manager/src/dev-tools/FeatureFlagTool.tsx +++ b/packages/manager/src/dev-tools/FeatureFlagTool.tsx @@ -32,6 +32,8 @@ const options: { flag: keyof Flags; label: string }[] = [ { flag: 'placementGroups', label: 'Placement Groups' }, { flag: 'selfServeBetas', label: 'Self Serve Betas' }, { flag: 'supportTicketSeverity', label: 'Support Ticket Severity' }, + { flag: 'dbaasV2', label: 'Databases V2 Beta' }, + { flag: 'databaseResize', label: 'Database Resize' }, ]; export const FeatureFlagTool = withFeatureFlagProvider(() => { diff --git a/packages/manager/src/factories/account.ts b/packages/manager/src/factories/account.ts index eff241f37d5..241deeea45f 100644 --- a/packages/manager/src/factories/account.ts +++ b/packages/manager/src/factories/account.ts @@ -45,7 +45,7 @@ export const accountFactory = Factory.Sync.makeFactory({ 'Linodes', 'LKE HA Control Planes', 'Machine Images', - 'Managed Databases', + 'Managed Databases V2', 'NodeBalancers', 'Object Storage Access Key Regions', 'Object Storage Endpoint Types', diff --git a/packages/manager/src/factories/databases.ts b/packages/manager/src/factories/databases.ts index b9188b54d62..87ee964126f 100644 --- a/packages/manager/src/factories/databases.ts +++ b/packages/manager/src/factories/databases.ts @@ -1,17 +1,20 @@ -import { +import { v4 } from 'uuid'; + +import Factory from 'src/factories/factoryProxy'; +import { pickRandom, randomDate } from 'src/utilities/random'; + +import type { + ClusterSize, Database, DatabaseBackup, DatabaseEngine, DatabaseInstance, DatabaseStatus, DatabaseType, + Engine, MySQLReplicationType, PostgresReplicationType, } from '@linode/api-v4/lib/databases/types'; -import Factory from 'src/factories/factoryProxy'; -import { v4 } from 'uuid'; - -import { pickRandom, randomDate } from 'src/utilities/random'; // These are not all of the possible statuses, but these are some common ones. export const possibleStatuses: DatabaseStatus[] = [ @@ -35,10 +38,35 @@ export const possiblePostgresReplicationTypes: PostgresReplicationType[] = [ 'asynch', ]; +export const possibleTypes: string[] = [ + 'g6-nanode-1', + 'g6-standard-2', + 'g6-standard-4', + 'g6-standard-6', + 'g6-standard-20', + 'g6-dedicated-32', + 'g6-dedicated-50', + 'g6-dedicated-56', + 'g6-dedicated-64', +]; + +export const possibleRegions: string[] = [ + 'ap-south', + 'ap-southeast', + 'ap-west', + 'ca-central', + 'eu-central', + 'fr-par', + 'us-east', + 'us-iad', + 'us-ord', +]; + export const IPv4List = ['192.0.2.1', '196.0.0.0', '198.0.0.2']; export const databaseTypeFactory = Factory.Sync.makeFactory({ class: 'standard', + disk: Factory.each((i) => i * 20480), engines: { mongodb: [ { @@ -134,32 +162,38 @@ export const databaseTypeFactory = Factory.Sync.makeFactory({ ], }, id: Factory.each((i) => `g6-standard-${i}`), - disk: Factory.each((i) => i * 20480), label: Factory.each((i) => `Linode ${i} GB`), memory: Factory.each((i) => i * 2048), vcpus: Factory.each((i) => i * 2), }); +const adb10 = (i: number) => i % 2 === 0; + export const databaseInstanceFactory = Factory.Sync.makeFactory( { - cluster_size: Factory.each(() => pickRandom([1, 3])), + cluster_size: Factory.each((i) => + adb10(i) + ? ([1, 3][i % 2] as ClusterSize) + : ([1, 2, 3][i % 3] as ClusterSize) + ), created: '2021-12-09T17:15:12', - engine: 'mysql', + engine: Factory.each((i) => ['mysql', 'postgresql'][i % 2] as Engine), hosts: { - primary: 'db-mysql-primary-0.b.linodeb.net', - secondary: 'db-mysql-secondary-0.b.linodeb.net', + primary: 'db-primary-0.b.linodeb.net', + secondary: 'db-secondary-0.b.linodeb.net', }, id: Factory.each((i) => i), instance_uri: '', - label: Factory.each((i) => `database-${i}`), + label: Factory.each((i) => `example.com-database-${i}`), members: { '2.2.2.2': 'primary', }, - region: 'us-east', - status: Factory.each(() => pickRandom(possibleStatuses)), - type: databaseTypeFactory.build().id, + platform: Factory.each((i) => (adb10(i) ? 'adb10' : 'adb20')), + region: Factory.each((i) => possibleRegions[i % possibleRegions.length]), + status: Factory.each((i) => possibleStatuses[i % possibleStatuses.length]), + type: Factory.each((i) => possibleTypes[i % possibleTypes.length]), updated: '2021-12-16T17:15:12', - version: '5.8.13', + version: Factory.each((i) => ['8.0.30', '15.7'][i % 2]), } ); @@ -189,7 +223,7 @@ export const databaseFactory = Factory.Sync.makeFactory({ ssl_connection: false, status: pickRandom(possibleStatuses), total_disk_size_gb: 15, - type: 'g6-standard-0', + type: 'g6-nanode-1', updated: '2021-12-16T17:15:12', updates: { day_of_week: 1, diff --git a/packages/manager/src/featureFlags.ts b/packages/manager/src/featureFlags.ts index 1e8c9db05dd..fc9989379ae 100644 --- a/packages/manager/src/featureFlags.ts +++ b/packages/manager/src/featureFlags.ts @@ -90,6 +90,7 @@ export interface Flags { databaseBeta: boolean; databaseResize: boolean; databases: boolean; + dbaasV2: BetaFeatureFlag; disableLargestGbPlans: boolean; eventMessagesV2: boolean; gecko: boolean; // @TODO gecko: delete this after next release diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx index db6e037c3dd..349347ce978 100644 --- a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx @@ -76,6 +76,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ }, }, chip: { + marginLeft: 6, marginTop: 4, }, createBtn: { @@ -452,7 +453,7 @@ const DatabaseCreate = () => { }, ], labelOptions: { - suffixComponent: flags.databaseBeta ? ( + suffixComponent: flags.dbaasV2?.beta ? ( ) : null, }, @@ -564,21 +565,6 @@ const DatabaseCreate = () => { ))} - - {flags.databaseBeta ? ( - - - Notice: There is no charge for database clusters during beta. - {' '} - Database clusters will be subject to charges when the beta - period ends on May 2nd, 2022.{' '} - - View pricing - - . - - ) : undefined} - diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.test.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.test.tsx index 820807f5139..f9ed2e10136 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.test.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.test.tsx @@ -38,7 +38,7 @@ describe('database resize', () => { const standardTypes = [ databaseTypeFactory.build({ class: 'nanode', - id: 'g6-standard-0', + id: 'g6-nanode-1', label: `Nanode 1 GB`, memory: 1024, }), @@ -75,7 +75,7 @@ describe('database resize', () => { const standardTypes = [ databaseTypeFactory.build({ class: 'nanode', - id: 'g6-standard-0', + id: 'g6-nanode-1', label: `Nanode 1 GB`, memory: 1024, }), diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResizeCurrentConfiguration.test.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResizeCurrentConfiguration.test.tsx index e997f5786cf..5c59d59b338 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResizeCurrentConfiguration.test.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResizeCurrentConfiguration.test.tsx @@ -28,7 +28,7 @@ describe('database current configuration section', () => { const standardTypes = [ databaseTypeFactory.build({ class: 'nanode', - id: 'g6-standard-0', + id: 'g6-nanode-1', label: `Nanode 1 GB`, memory: 1024, }), @@ -63,7 +63,7 @@ describe('database current configuration section', () => { getByText('1 GB'); getByText('CPUs'); - getByText('4'); + getByText('2'); getByText('Total Disk Size'); getByText('15 GB'); diff --git a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLogo.tsx b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLogo.tsx new file mode 100644 index 00000000000..bce0caa0a2b --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLogo.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { makeStyles } from 'tss-react/mui'; + +import Logo from 'src/assets/icons/db-logo.svg'; +import { BetaChip } from 'src/components/BetaChip/BetaChip'; +import { Box } from 'src/components/Box'; +import { Typography } from 'src/components/Typography'; + +import type { Theme } from '@mui/material/styles'; + +interface Props { + style?: React.CSSProperties; +} + +const useStyles = makeStyles()((theme: Theme) => ({ + betaChip: { + backgroundColor: '#727272', + color: theme.color.white, + }, + logo: { + color: '#32363C', + display: 'flex', + marginTop: '8px', + }, +})); + +export const DatabaseLogo = ({ style }: Props) => { + const { classes } = useStyles(); + return ( + + + + + Powered by + + + + ); +}; diff --git a/packages/manager/src/features/Databases/utilities.test.ts b/packages/manager/src/features/Databases/utilities.test.ts index c7171996fcb..dd981780f18 100644 --- a/packages/manager/src/features/Databases/utilities.test.ts +++ b/packages/manager/src/features/Databases/utilities.test.ts @@ -8,7 +8,7 @@ import { wrapWithTheme } from 'src/utilities/testHelpers'; import { useIsDatabasesEnabled } from './utilities'; describe('useIsDatabasesEnabled', () => { - it('should return true for an unrestricted user with the account capability', async () => { + it('should return true for an unrestricted user with the account capability V1', async () => { const account = accountFactory.build({ capabilities: ['Managed Databases'], }); @@ -20,10 +20,42 @@ describe('useIsDatabasesEnabled', () => { ); const { result } = renderHook(() => useIsDatabasesEnabled(), { - wrapper: wrapWithTheme, + wrapper: (ui) => + wrapWithTheme(ui, { + flags: { dbaasV2: { beta: false, enabled: false } }, + }), }); - await waitFor(() => expect(result.current.isDatabasesEnabled).toBe(true)); + await waitFor(() => { + expect(result.current.isDatabasesEnabled).toBe(true); + expect(result.current.isDatabasesV1Enabled).toBe(true); + expect(result.current.isDatabasesV2Enabled).toBe(false); + }); + }); + + it('should return true for an unrestricted user with the account capability V2', async () => { + const account = accountFactory.build({ + capabilities: ['Managed Databases V2'], + }); + + server.use( + http.get('*/v4/account', () => { + return HttpResponse.json(account); + }) + ); + + const { result } = renderHook(() => useIsDatabasesEnabled(), { + wrapper: (ui) => + wrapWithTheme(ui, { + flags: { dbaasV2: { beta: true, enabled: true } }, + }), + }); + + await waitFor(() => { + expect(result.current.isDatabasesEnabled).toBe(true); + expect(result.current.isDatabasesV1Enabled).toBe(false); + expect(result.current.isDatabasesV2Enabled).toBe(true); + }); }); it('should return false for an unrestricted user without the account capability', async () => { diff --git a/packages/manager/src/features/Databases/utilities.ts b/packages/manager/src/features/Databases/utilities.ts index 6fa5593aeaa..73e64f1f8e3 100644 --- a/packages/manager/src/features/Databases/utilities.ts +++ b/packages/manager/src/features/Databases/utilities.ts @@ -1,3 +1,4 @@ +import { useFlags } from 'src/hooks/useFlags'; import { useAccount } from 'src/queries/account/account'; import { useDatabaseEnginesQuery } from 'src/queries/databases/databases'; @@ -24,10 +25,21 @@ export const useIsDatabasesEnabled = () => { const checkRestrictedUser = !account; const { data: engines } = useDatabaseEnginesQuery(checkRestrictedUser); + const flags = useFlags(); if (account) { + const isDatabasesV1Enabled = account.capabilities.includes( + 'Managed Databases' + ); + + const isDatabasesV2Enabled = + account.capabilities.includes('Managed Databases V2') && + flags.dbaasV2?.enabled; + return { - isDatabasesEnabled: account.capabilities.includes('Managed Databases'), + isDatabasesEnabled: isDatabasesV1Enabled || isDatabasesV2Enabled, + isDatabasesV1Enabled, + isDatabasesV2Enabled, }; } diff --git a/packages/manager/src/mocks/serverHandlers.ts b/packages/manager/src/mocks/serverHandlers.ts index 5d66dcc0d6b..f7e7af06432 100644 --- a/packages/manager/src/mocks/serverHandlers.ts +++ b/packages/manager/src/mocks/serverHandlers.ts @@ -189,7 +189,7 @@ const entityTransfers = [ const databases = [ http.get('*/databases/instances', () => { - const databases = databaseInstanceFactory.buildList(5); + const databases = databaseInstanceFactory.buildList(9); return HttpResponse.json(makeResourcePage(databases)); }),