Skip to content

Commit 6ce19a9

Browse files
committed
feat(middleware-bucket-endpoint): generate outpost endpoints
1 parent 17620d6 commit 6ce19a9

File tree

2 files changed

+35
-14
lines changed

2 files changed

+35
-14
lines changed

packages/middleware-bucket-endpoint/src/bucketHostname.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
ArnHostnameParams,
33
BucketHostnameParams,
44
DOT_PATTERN,
5-
getAccessPointName,
5+
getArnResources,
66
getSuffix,
77
getSuffixForArnEndpoint,
88
isBucketNameOptions,
@@ -69,12 +69,15 @@ const getEndpointFromAccessPoint = (options: ArnHostnameParams): string => {
6969
validateRegion(region, { useArnRegion, clientRegion, clientSigningRegion });
7070
validatePartition(partition, { clientPartition });
7171
validateAccountId(accountId);
72-
const accessPointName = getAccessPointName(resource);
73-
validateDNSHostLabel(`${accessPointName}-${accountId}`, { tlsCompatible });
7472

75-
return `${accessPointName}-${accountId}.s3-accesspoint${dualstackEndpoint ? ".dualstack" : ""}.${
76-
useArnRegion ? region : clientRegion
77-
}.${hostnameSuffix}`;
73+
const { accesspointName, outpostId } = getArnResources(resource);
74+
validateDNSHostLabel(`${accesspointName}-${accountId}`, { tlsCompatible });
75+
const endpointRegion = useArnRegion ? region : clientRegion;
76+
return outpostId
77+
? `${accesspointName}-${accountId}.${outpostId}.s3-outposts.${endpointRegion}.${hostnameSuffix}`
78+
: `${accesspointName}-${accountId}.s3-accesspoint${
79+
dualstackEndpoint ? ".dualstack" : ""
80+
}.${endpointRegion}.${hostnameSuffix}`;
7881
};
7982

8083
const getEndpointFromBucketName = ({

packages/middleware-bucket-endpoint/src/bucketHostnameUtils.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,31 @@ export const validateDNSHostLabel = (label: string, options: { tlsCompatible?: b
116116
}
117117
};
118118

119-
export const getAccessPointName = (resource: string): string => {
120-
if (resource.indexOf("accesspoint:") !== 0 && resource.indexOf("accesspoint/") !== 0) {
121-
throw new Error("Access point ARN resource should begin with 'accesspoint/'");
119+
export const getArnResources = (
120+
resource: string
121+
): {
122+
accesspointName: string;
123+
outpostId?: string;
124+
} => {
125+
const delimiter = resource.includes(":") ? ":" : "/";
126+
const [resourceType, ...rest] = resource.split(delimiter);
127+
if (resourceType === "accesspoint") {
128+
// Parse accesspoint ARN
129+
if (rest.length !== 1 || rest[0] === "") {
130+
throw new Error("Access Point ARN should have one resource accesspoint/{accesspointname}");
131+
}
132+
return { accesspointName: rest[0] };
133+
} else if (resourceType === "outpost") {
134+
// Parse outpost ARN
135+
if (!rest[0] || rest[1] !== "accesspoint" || !rest[2] || rest.length !== 3) {
136+
throw new Error("Outpost ARN should have resource outpost/{outpostId}/accesspoint/{accesspointName}");
137+
}
138+
const [outpostId, _, accesspointName] = rest;
139+
if (!/op-[0-9a-fA-F]{17}/.test(outpostId)) throw new Error("Outpost Id must follow pattern /op-[0-9a-fA-F]{17}/");
140+
if (!/[0-9a-zA-Z-]{3,50}/.test(accesspointName))
141+
throw new Error("Accesspoint name must follow pattern /[0-9a-zA-Z-]{3,50}/");
142+
return { outpostId, accesspointName };
143+
} else {
144+
throw new Error(`ARN resource should begin with 'accesspoint${delimiter}' or 'outpost${delimiter}'`);
122145
}
123-
const parsedResource = resource.split(resource["accesspoint".length]);
124-
if (parsedResource.length !== 2 || parsedResource[1] === "") {
125-
throw new Error("Access Point ARN should have one resource accesspoint/{accesspointname}");
126-
}
127-
return parsedResource[1];
128146
};

0 commit comments

Comments
 (0)