diff --git a/packages/manager/.changeset/pr-10771-upcoming-features-1723731588900.md b/packages/manager/.changeset/pr-10771-upcoming-features-1723731588900.md new file mode 100644 index 00000000000..4852e4ed0c6 --- /dev/null +++ b/packages/manager/.changeset/pr-10771-upcoming-features-1723731588900.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Add Endpoint Type Column & Disable CORS for Gen2 Buckets ([#10771](https://github.com/linode/manager/pull/10771)) diff --git a/packages/manager/src/features/ObjectStorage/BucketDetail/AccessSelect.tsx b/packages/manager/src/features/ObjectStorage/BucketDetail/AccessSelect.tsx index 0745256bde8..e77dd8eb178 100644 --- a/packages/manager/src/features/ObjectStorage/BucketDetail/AccessSelect.tsx +++ b/packages/manager/src/features/ObjectStorage/BucketDetail/AccessSelect.tsx @@ -20,11 +20,13 @@ import { copy } from './AccessSelect.data'; import type { ACLType, ObjectStorageBucketAccess, + ObjectStorageEndpointTypes, ObjectStorageObjectACL, } from '@linode/api-v4/lib/object-storage'; import type { Theme } from '@mui/material/styles'; export interface Props { + endpointType?: ObjectStorageEndpointTypes; getAccess: () => Promise; name: string; updateAccess: (acl: ACLType, cors_enabled?: boolean) => Promise<{}>; @@ -38,7 +40,7 @@ function isUpdateObjectStorageBucketAccessPayload( } export const AccessSelect = React.memo((props: Props) => { - const { getAccess, name, updateAccess, variant } = props; + const { endpointType, getAccess, name, updateAccess, variant } = props; // Access data for this Object (from the API). const [aclData, setACLData] = React.useState(null); const [corsData, setCORSData] = React.useState(true); @@ -139,6 +141,9 @@ export const AccessSelect = React.memo((props: Props) => { ? 'CORS Enabled' : 'CORS Disabled'; + const isCorsEnabled = + variant === 'bucket' && endpointType !== 'E2' && endpointType !== 'E3'; + const selectedOption = _options.find((thisOption) => thisOption.value === selectedACL) ?? _options.find((thisOption) => thisOption.value === 'private'); @@ -180,7 +185,7 @@ export const AccessSelect = React.memo((props: Props) => { ) : null} - {variant === 'bucket' ? ( + {isCorsEnabled ? ( { /> ) : null} - {variant === 'bucket' ? ( + {isCorsEnabled ? ( Whether Cross-Origin Resource Sharing is enabled for all origins. For more fine-grained control of CORS, please use another{' '} diff --git a/packages/manager/src/features/ObjectStorage/BucketDetail/BucketDetail.tsx b/packages/manager/src/features/ObjectStorage/BucketDetail/BucketDetail.tsx index 96c41fcb9da..a72be5b389b 100644 --- a/packages/manager/src/features/ObjectStorage/BucketDetail/BucketDetail.tsx +++ b/packages/manager/src/features/ObjectStorage/BucketDetail/BucketDetail.tsx @@ -56,6 +56,7 @@ import ObjectTableContent from './ObjectTableContent'; import type { ObjectStorageClusterID, + ObjectStorageEndpointTypes, ObjectStorageObject, ObjectStorageObjectList, } from '@linode/api-v4'; @@ -64,8 +65,12 @@ interface MatchParams { bucketName: string; clusterId: ObjectStorageClusterID; } +interface Props { + endpointType: ObjectStorageEndpointTypes; +} -export const BucketDetail = () => { +export const BucketDetail = (props: Props) => { + const { endpointType } = props; /** * @note If `Object Storage Access Key Regions` is enabled, clusterId will actually contain * the bucket's region id @@ -473,6 +478,7 @@ export const BucketDetail = () => { bucketName={bucketName} clusterId={clusterId} displayName={selectedObject?.name} + endpointType={endpointType} lastModified={selectedObject?.last_modified} name={selectedObject?.name} onClose={closeObjectDetailsDrawer} diff --git a/packages/manager/src/features/ObjectStorage/BucketDetail/ObjectDetailsDrawer.tsx b/packages/manager/src/features/ObjectStorage/BucketDetail/ObjectDetailsDrawer.tsx index 8deaf658dcd..4ea8cb5854b 100644 --- a/packages/manager/src/features/ObjectStorage/BucketDetail/ObjectDetailsDrawer.tsx +++ b/packages/manager/src/features/ObjectStorage/BucketDetail/ObjectDetailsDrawer.tsx @@ -17,12 +17,16 @@ import { readableBytes } from 'src/utilities/unitConversions'; import { AccessSelect } from './AccessSelect'; -import type { ACLType } from '@linode/api-v4/lib/object-storage'; +import type { + ACLType, + ObjectStorageEndpointTypes, +} from '@linode/api-v4/lib/object-storage'; export interface ObjectDetailsDrawerProps { bucketName: string; clusterId: string; displayName?: string; + endpointType?: ObjectStorageEndpointTypes; lastModified?: null | string; name?: string; onClose: () => void; @@ -38,6 +42,7 @@ export const ObjectDetailsDrawer = React.memo( bucketName, clusterId, displayName, + endpointType, lastModified, name, onClose, @@ -55,6 +60,9 @@ export const ObjectDetailsDrawer = React.memo( } } catch {} + const isAccessSelectEnabled = + open && name && endpointType !== 'E2' && endpointType !== 'E3'; + return ( ) : null} - {open && name ? ( + {isAccessSelectEnabled ? ( <> > = React.lazy(() => import('./BucketDetail').then((module) => ({ default: module.BucketDetail })) @@ -31,11 +36,27 @@ interface MatchProps { type Props = RouteComponentProps; export const BucketDetailLanding = React.memo((props: Props) => { + const { data: account } = useAccount(); + const flags = useFlags(); + + const isObjectStorageGen2Enabled = isFeatureEnabledV2( + 'Object Storage Endpoint Types', + Boolean(flags.objectStorageGen2?.enabled), + account?.capabilities ?? [] + ); + + const { data: bucketsData } = useObjectStorageBuckets( + isObjectStorageGen2Enabled + ); + const matches = (p: string) => { return Boolean(matchPath(p, { path: props.location.pathname })); }; const { bucketName, clusterId } = props.match.params; + const { endpoint_type: endpointType } = + bucketsData?.buckets.find(({ label }) => label === bucketName) ?? {}; + const tabs = [ { routeName: `${props.match.url}/objects`, @@ -85,7 +106,7 @@ export const BucketDetailLanding = React.memo((props: Props) => { }> - + diff --git a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketDetailsDrawer.tsx b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketDetailsDrawer.tsx index c9d1968d1ed..7139165f273 100644 --- a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketDetailsDrawer.tsx +++ b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketDetailsDrawer.tsx @@ -24,12 +24,16 @@ import { readableBytes } from 'src/utilities/unitConversions'; import { AccessSelect } from '../BucketDetail/AccessSelect'; import type { Region } from '@linode/api-v4'; -import type { ACLType } from '@linode/api-v4/lib/object-storage'; +import type { + ACLType, + ObjectStorageEndpointTypes, +} from '@linode/api-v4/lib/object-storage'; export interface BucketDetailsDrawerProps { bucketLabel?: string; bucketRegion?: Region; cluster?: string; created?: string; + endpointType?: ObjectStorageEndpointTypes; hostname?: string; objectsNumber?: number; onClose: () => void; @@ -44,6 +48,7 @@ export const BucketDetailsDrawer = React.memo( bucketRegion, cluster, created, + endpointType, hostname, objectsNumber, onClose, @@ -89,6 +94,11 @@ export const BucketDetailsDrawer = React.memo( Created: {formattedCreated} ) : null} + {Boolean(endpointType) && ( + + Endpoint Type: {endpointType} + + )} {isObjMultiClusterEnabled ? ( {bucketRegion?.label} @@ -155,6 +165,7 @@ export const BucketDetailsDrawer = React.memo( payload ); }} + endpointType={endpointType} name={bucketLabel} variant="bucket" /> diff --git a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLanding.tsx b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLanding.tsx index 2abf39ac90d..55bd399c504 100644 --- a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLanding.tsx +++ b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketLanding.tsx @@ -238,6 +238,7 @@ export const BucketLanding = () => { bucketLabel={bucketForDetails?.label} cluster={bucketForDetails?.cluster} created={bucketForDetails?.created} + endpointType={bucketForDetails?.endpoint_type} hostname={bucketForDetails?.hostname} objectsNumber={bucketForDetails?.objects} onClose={closeBucketDetailDrawer} diff --git a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketTable.tsx b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketTable.tsx index 4b53b9a89a2..781eef74551 100644 --- a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketTable.tsx +++ b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketTable.tsx @@ -1,4 +1,3 @@ -import { ObjectStorageBucket } from '@linode/api-v4/lib/object-storage'; import * as React from 'react'; import { Hidden } from 'src/components/Hidden'; @@ -13,6 +12,8 @@ import { TableSortCell } from 'src/components/TableSortCell'; import { BucketTableRow } from './BucketTableRow'; +import type { ObjectStorageBucket } from '@linode/api-v4/lib/object-storage'; + interface Props { data: ObjectStorageBucket[]; handleClickDetails: (bucket: ObjectStorageBucket) => void; @@ -32,6 +33,8 @@ export const BucketTable = (props: Props) => { orderBy, } = props; + const isEndpointTypeAvailable = Boolean(data[0]?.endpoint_type); + return ( {({ @@ -66,6 +69,19 @@ export const BucketTable = (props: Props) => { Region + {isEndpointTypeAvailable && ( + + + Endpoint Type + + + )} void; onRemove: () => void; @@ -33,6 +34,7 @@ export const BucketTableRow = (props: BucketTableRowProps) => { const { cluster, created, + endpoint_type, hostname, label, objects, @@ -62,6 +64,9 @@ export const BucketTableRow = (props: BucketTableRowProps) => { const regionsLookup = regions && getRegionsByRegionId(regions); + const isLegacy = endpoint_type === 'E0'; + const typeLabel = isLegacy ? 'Legacy' : 'Standard'; + return ( @@ -92,6 +97,15 @@ export const BucketTableRow = (props: BucketTableRowProps) => { + {Boolean(endpoint_type) && ( + + + + {typeLabel} ({endpoint_type}) + + + + )}