Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bd5dda2
fix: [M3-9022] - Filter regions by endpoints and fix unavailable regions
jaalah Dec 17, 2024
aa399e8
Add changesets
jaalah Dec 17, 2024
38e6d86
Bump package.json
jaalah Dec 17, 2024
319772f
Update changelog
jaalah Dec 17, 2024
a274a1a
Merge branch 'staging' of github.com:linode/manager into M3-9022
jaalah Dec 17, 2024
110c529
Add filtering for access key create
jaalah Dec 18, 2024
59a1844
Add gen2 regions to whitelist to bypass filters
jaalah Dec 18, 2024
2baabcb
Add ability to use sx prop for helper text
jaalah Dec 18, 2024
266d330
Adjust spacing
jaalah Dec 18, 2024
4dbcf16
Added changeset: `sxHelperText` prop to `TextField` component to cont…
jaalah Dec 18, 2024
f4ea05e
Latest changes @alban-akamai
jaalah Dec 18, 2024
cfd79b3
Comment update
jaalah Dec 18, 2024
c61f83a
Feature flag whitelist
jaalah Dec 18, 2024
daa2cfa
Using the key which is actually the label is not unique. Must use id
jaalah Dec 19, 2024
3eb4ebc
Using the key which is actually the label is not unique. Must use id
jaalah Dec 19, 2024
55ac380
Revert "Using the key which is actually the label is not unique. Must…
jaalah Dec 19, 2024
47a03bc
Using the key which is actually the label is not unique. Must use id
jaalah Dec 19, 2024
caeff0b
Remove sxHelperText @alban-akamai
jaalah Dec 19, 2024
ce7a06f
Merge pull request #11432 from jaalah-akamai/M3-9022
jaalah-akamai Dec 19, 2024
0018d37
Merge branch 'master' into staging
jaalah-akamai Dec 19, 2024
f83171e
Update packages/manager/CHANGELOG.md
jaalah-akamai Dec 19, 2024
a5e523d
Add gen2 flag to access key drawer - filter regions
jaalah Dec 19, 2024
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
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-19] - 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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import CloseIcon from '@mui/icons-material/Close';
import React from 'react';

import { Flag } from 'src/components/Flag';
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 @@ -50,12 +51,18 @@ export const RegionMultiSelect = React.memo((props: RegionMultiSelectProps) => {
...rest
} = props;

const { isObjectStorageGen2Enabled } = useIsObjectStorageGen2Enabled();

const {
data: accountAvailability,
isLoading: accountAvailabilityLoading,
} = useAllAccountAvailabilitiesQuery();

const regionOptions = getRegionOptions({ currentCapability, regions });
const regionOptions = getRegionOptions({
currentCapability,
isObjectStorageGen2Enabled,
regions,
});

const selectedRegions = regionOptions.filter((r) =>
selectedIds.includes(r.id)
Expand Down
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
@@ -1,7 +1,7 @@
import * as React from 'react';

import { RegionMultiSelect } from 'src/components/RegionSelect/RegionMultiSelect';
import { useRegionsQuery } from 'src/queries/regions/regions';
import { useObjectStorageRegions } from 'src/features/ObjectStorage/hooks/useObjectStorageRegions';
import { sortByString } from 'src/utilities/sort-by';

import type { Region } from '@linode/api-v4';
Expand All @@ -22,10 +22,13 @@ const sortRegionOptions = (a: Region, b: Region) => {
export const AccessKeyRegions = (props: Props) => {
const { disabled, error, 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 (
<RegionMultiSelect
Expand All @@ -38,7 +41,7 @@ export const AccessKeyRegions = (props: Props) => {
isClearable={false}
label="Regions"
onChange={onChange}
regions={regions ?? []}
regions={availableStorageRegions ?? []}
required={required}
selectedIds={selectedRegion}
sortRegionOptions={sortRegionOptions}
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
Loading
Loading