Skip to content

Commit

Permalink
Merge pull request #11432 from jaalah-akamai/M3-9022
Browse files Browse the repository at this point in the history
fix: [M3-9022, M3-9027] - Filter regions by endpoints and fix unavailable regions
  • Loading branch information
jaalah-akamai authored Dec 19, 2024
2 parents 500475c + caeff0b commit ce7a06f
Show file tree
Hide file tree
Showing 16 changed files with 267 additions and 76 deletions.
8 changes: 8 additions & 0 deletions packages/manager/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).

## [2024-12-18] - v1.133.1

### Fixed:

- Filter available regions in Object Gen2 Create Drawer and Access Keys List based on endpoint capabilities ([#11432](https://github.com/linode/manager/pull/11432))
- Region name display in Gen2 warning notices when regions are unavailable due to format mismatch ([#11432](https://github.com/linode/manager/pull/11432))


## [2024-12-10] - v1.133.0

### Added:
Expand Down
2 changes: 1 addition & 1 deletion packages/manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "linode-manager",
"author": "Linode",
"description": "The Linode Manager website",
"version": "1.133.0",
"version": "1.133.1",
"private": true,
"type": "module",
"bugs": {
Expand Down
7 changes: 5 additions & 2 deletions packages/manager/src/components/RegionSelect/RegionSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Autocomplete } from '@linode/ui';
import { createFilterOptions } from '@mui/material/Autocomplete';
import * as React from 'react';

import { Autocomplete } from '@linode/ui';
import { Flag } from 'src/components/Flag';
import { useIsGeckoEnabled } from 'src/components/RegionSelect/RegionSelect.utils';
import { useIsObjectStorageGen2Enabled } from 'src/features/ObjectStorage/hooks/useIsObjectStorageGen2Enabled';
import { useAllAccountAvailabilitiesQuery } from 'src/queries/account/availability';
import { getRegionCountryGroup } from 'src/utilities/formatRegion';

Expand Down Expand Up @@ -53,6 +54,7 @@ export const RegionSelect = <
} = props;

const { isGeckoLAEnabled } = useIsGeckoEnabled();
const { isObjectStorageGen2Enabled } = useIsObjectStorageGen2Enabled();

const {
data: accountAvailability,
Expand All @@ -61,6 +63,7 @@ export const RegionSelect = <

const regionOptions = getRegionOptions({
currentCapability,
isObjectStorageGen2Enabled,
regionFilter,
regions,
});
Expand Down Expand Up @@ -114,7 +117,7 @@ export const RegionSelect = <
<RegionOption
disabledOptions={disabledRegions[region.id]}
item={region}
key={key}
key={`${region.id}-${key}`}
props={rest}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,35 @@ const NORTH_AMERICA = CONTINENT_CODE_TO_CONTINENT.NA;

interface RegionSelectOptionsOptions {
currentCapability: Capabilities | undefined;
/**
* @TODO: This is a temporary property to gate the whitelisted regions to Gen2 users
*/
isObjectStorageGen2Enabled?: boolean;
regionFilter?: RegionFilterValue;
regions: Region[];
}

// @TODO: OBJ Gen2: This should be removed once these regions obtain the `Object Storage` capability.
const WHITELISTED_REGIONS = new Set([
'gb-lon',
'au-mel',
'in-bom-2',
'de-fra-2',
'sg-sin-2',
]);

export const getRegionOptions = ({
currentCapability,
isObjectStorageGen2Enabled,
regionFilter,
regions,
}: RegionSelectOptionsOptions) => {
return regions
.filter((region) => {
if (isObjectStorageGen2Enabled && WHITELISTED_REGIONS.has(region.id)) {
return true;
}

if (
currentCapability &&
!region.capabilities.includes(currentCapability)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import React from 'react';

import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip';
import { TableCell } from 'src/components/TableCell';
import { useRegionsQuery } from 'src/queries/regions/regions';
import { useObjectStorageRegions } from 'src/features/ObjectStorage/hooks/useObjectStorageRegions';
import { pluralize } from 'src/utilities/pluralize';
import { getRegionsByRegionId } from 'src/utilities/regions';

import type { ObjectStorageKey, ObjectStorageKeyRegions } from '@linode/api-v4';

Expand All @@ -19,17 +18,20 @@ interface Props {
export const HostNameTableCell = (props: Props) => {
const { setHostNames, setShowHostNamesDrawers, storageKeyData } = props;

const { data: regionsData } = useRegionsQuery();

const regionsLookup = regionsData && getRegionsByRegionId(regionsData);
const { availableStorageRegions, regionsByIdMap } = useObjectStorageRegions();

const { regions } = storageKeyData;

if (!regionsLookup || !regionsData || !regions || regions.length === 0) {
if (
!regionsByIdMap ||
!availableStorageRegions ||
!regions ||
regions.length === 0
) {
return <TableCell>None</TableCell>;
}
const formatEndpoint = (region: ObjectStorageKeyRegions) => {
const label = regionsLookup[region.id]?.label;
const label = regionsByIdMap[region.id]?.label;
const endpointType = region.endpoint_type
? ` (${region.endpoint_type})`
: '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { TableCell } from 'src/components/TableCell';
import { TableHead } from 'src/components/TableHead';
import { TableRow } from 'src/components/TableRow';
import { TableRowEmpty } from 'src/components/TableRowEmpty/TableRowEmpty';
import { useRegionsQuery } from 'src/queries/regions/regions';
import { getRegionsByRegionId } from 'src/utilities/regions';
import { useObjectStorageRegions } from 'src/features/ObjectStorage/hooks/useObjectStorageRegions';

import { AccessCell } from './AccessCell';
import {
Expand Down Expand Up @@ -58,11 +57,9 @@ interface Props {

export const BucketPermissionsTable = React.memo((props: Props) => {
const { bucket_access, checked, mode, selectedRegions, updateScopes } = props;
const { regionsByIdMap } = useObjectStorageRegions();

const { data: regionsData } = useRegionsQuery();
const regionsLookup = regionsData && getRegionsByRegionId(regionsData);

if (!bucket_access || !regionsLookup) {
if (!bucket_access || !regionsByIdMap) {
return null;
}

Expand Down Expand Up @@ -185,7 +182,7 @@ export const BucketPermissionsTable = React.memo((props: Props) => {
padding="checkbox"
sx={{ minWidth: '150px' }}
>
{regionsLookup[thisScope.region ?? '']?.label}
{regionsByIdMap[thisScope.region ?? '']?.label}
</StyledClusterCell>
<StyledBucketCell padding="checkbox">
{thisScope.bucket_name}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import * as React from 'react';

import { CopyableTextField } from 'src/components/CopyableTextField/CopyableTextField';
import { Drawer } from 'src/components/Drawer';
import { useRegionsQuery } from 'src/queries/regions/regions';
import { getRegionsByRegionId } from 'src/utilities/regions';
import { useObjectStorageRegions } from 'src/features/ObjectStorage/hooks/useObjectStorageRegions';

import { CopyAllHostnames } from './CopyAllHostnames';

Expand All @@ -18,10 +17,9 @@ interface Props {

export const HostNamesDrawer = (props: Props) => {
const { onClose, open, regions } = props;
const { data: regionsData } = useRegionsQuery();
const regionsLookup = regionsData && getRegionsByRegionId(regionsData);
const { availableStorageRegions, regionsByIdMap } = useObjectStorageRegions();

if (!regionsData || !regionsLookup) {
if (!availableStorageRegions || !regionsByIdMap) {
return null;
}

Expand All @@ -32,7 +30,7 @@ export const HostNamesDrawer = (props: Props) => {
text={
regions
.map((region) => {
const label = regionsLookup[region.id]?.label;
const label = regionsByIdMap[region.id]?.label;
const endpointType = region.endpoint_type
? ` (${region.endpoint_type})`
: '';
Expand Down Expand Up @@ -61,9 +59,9 @@ export const HostNamesDrawer = (props: Props) => {
border: 'none',
maxWidth: '100%',
}}
value={`${regionsLookup[region.id]?.label}${endpointTypeLabel}: ${
region.s3_endpoint
}`}
value={`${
regionsByIdMap[region.id]?.label
}${endpointTypeLabel}: ${region.s3_endpoint}`}
hideLabel
key={index}
label={`${region.id}${endpointTypeLabel}: ${region.s3_endpoint}`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ import React, { useEffect, useState } from 'react';
import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
import { Drawer } from 'src/components/Drawer';
import { Link } from 'src/components/Link';
import { useObjectStorageRegions } from 'src/features/ObjectStorage/hooks/useObjectStorageRegions';
import { useAccountSettings } from 'src/queries/account/settings';
import { useObjectStorageBuckets } from 'src/queries/object-storage/queries';
import { useRegionsQuery } from 'src/queries/regions/regions';
import { getRegionsByRegionId } from 'src/utilities/regions';
import { sortByString } from 'src/utilities/sort-by';

import { EnableObjectStorageModal } from '../EnableObjectStorageModal';
Expand Down Expand Up @@ -108,9 +107,7 @@ export const OMC_AccessKeyDrawer = (props: AccessKeyDrawerProps) => {
open,
} = props;

const { data: regions } = useRegionsQuery();

const regionsLookup = regions && getRegionsByRegionId(regions);
const { regionsByIdMap } = useObjectStorageRegions();

const {
data: objectStorageBuckets,
Expand Down Expand Up @@ -209,7 +206,7 @@ export const OMC_AccessKeyDrawer = (props: AccessKeyDrawerProps) => {
);
formik.setFieldValue(
'bucket_access',
getDefaultScopes(bucketsInRegions, regionsLookup)
getDefaultScopes(bucketsInRegions, regionsByIdMap)
);
};

Expand Down Expand Up @@ -292,10 +289,9 @@ export const OMC_AccessKeyDrawer = (props: AccessKeyDrawerProps) => {
);
formik.setFieldValue(
'bucket_access',
getDefaultScopes(bucketsInRegions, regionsLookup)
getDefaultScopes(bucketsInRegions, regionsByIdMap)
);
formik.setFieldValue('regions', values);
formik.setFieldTouched('regions', true, true);
}}
disabled={isRestrictedUser}
name="regions"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { screen } from '@testing-library/react';
import { screen, waitFor } from '@testing-library/react';
import * as React from 'react';

import {
Expand Down Expand Up @@ -169,4 +169,56 @@ describe('ObjectStorageLanding', () => {

await screen.findByText(/Total storage used: 10 GB/);
});

it('renders error notice for multiple regions', async () => {
objectStorageBucketFactory.resetSequenceNumber();
objectStorageClusterFactory.resetSequenceNumber();

// Create multiple down clusters in different regions
const downClusters = [
objectStorageClusterFactory.build({ region: 'us-west' }),
objectStorageClusterFactory.build({ region: 'ap-south' }),
objectStorageClusterFactory.build({ region: 'eu-west' }),
];

// Mock Clusters
server.use(
http.get('*/object-storage/clusters', () => {
const upCluster = objectStorageClusterFactory.build({
region: 'us-east',
});
return HttpResponse.json(
makeResourcePage([...downClusters, upCluster])
);
})
);

// Mock bucket errors for each down cluster
server.use(
...downClusters.map((cluster) =>
http.get(`*/object-storage/buckets/${cluster.id}`, () => {
return HttpResponse.json([{ reason: 'Cluster offline!' }], {
status: 500,
});
})
),
// Mock successful response for up cluster
http.get('*/object-storage/buckets/*', () => {
return HttpResponse.json(
makeResourcePage(
objectStorageBucketFactory.buildList(1, { cluster: 'us-east' })
)
);
})
);

renderWithTheme(<BucketLanding />);

await waitFor(() => {
const errorRegions = ['US, Fremont, CA', 'SG, Singapore', 'GB, London'];
for (const region of errorRegions) {
expect(screen.queryByText(region)).toBeInTheDocument();
}
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ export const BucketRateLimitTable = ({
<Box>
<FormLabel>
<Typography
{...typographyProps}
data-testid="bucketRateLimit"
marginBottom={1}
{...typographyProps}
marginTop={2}
>
Bucket Rate Limits
</Typography>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';

import { RegionSelect } from 'src/components/RegionSelect/RegionSelect';
import { useRegionsQuery } from 'src/queries/regions/regions';
import { useObjectStorageRegions } from 'src/features/ObjectStorage/hooks/useObjectStorageRegions';

interface Props {
disabled?: boolean;
Expand All @@ -15,10 +15,13 @@ interface Props {
export const BucketRegions = (props: Props) => {
const { disabled, error, onBlur, onChange, required, selectedRegion } = props;

const { data: regions, error: regionsError } = useRegionsQuery();
const {
allRegionsError,
availableStorageRegions,
} = useObjectStorageRegions();

// Error could be: 1. General Regions error, 2. Field error, 3. Nothing
const errorText = error || regionsError?.[0]?.reason;
const errorText = error || allRegionsError?.[0]?.reason;

return (
<RegionSelect
Expand All @@ -30,7 +33,7 @@ export const BucketRegions = (props: Props) => {
onBlur={onBlur}
onChange={(e, region) => onChange(region.id)}
placeholder="Select a Region"
regions={regions ?? []}
regions={availableStorageRegions ?? []}
required={required}
value={selectedRegion}
/>
Expand Down
Loading

0 comments on commit ce7a06f

Please sign in to comment.