Skip to content

[WIP] feat(config-resolver): resolve hostname from variants #2972

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5584692
chore(clients): update endpoints as of 2021/11/02
trivikr Nov 2, 2021
7ca6d07
chore(clients): populate variants in endpoints hashes
trivikr Nov 2, 2021
213c7c3
chore(functional): test fips by passing isFipsEndpoint: true
trivikr Nov 2, 2021
e23c71d
chore(config-resolver): add fips and dualstack config in getRegionInfo
trivikr Nov 2, 2021
70a446f
chore: wip to get hostname from variants
trivikr Nov 2, 2021
eb527c1
chore: wip 2 for reading hostname from variants
trivikr Nov 2, 2021
2b4298c
test(functional): pass isFipsEndpoint while calling using signingRegion
trivikr Nov 2, 2021
7b7dfb5
test(functional): do not test for signingRegion as it's same as region
trivikr Nov 2, 2021
7f75540
test(functional): run streams.dynamodb tests only for variants
trivikr Nov 2, 2021
cc06244
chore(functional): wip for dualstack tests
trivikr Nov 3, 2021
02d1389
chore(functional): wip 2
trivikr Nov 3, 2021
567dfe6
test(functional): fix bug in test case creation
trivikr Nov 3, 2021
fd8253c
test(functional): remove test cases where dualstack variant is not de…
trivikr Nov 3, 2021
e609a27
test(functional): remove test cases where dualstack is not supported …
trivikr Nov 3, 2021
b9680b0
test(functional): move iotsecuredtunneling to unsupported
trivikr Nov 3, 2021
f4613eb
test(functional): remove SimpleDB test as it's deprecated
trivikr Nov 3, 2021
2f6aeb7
test(functional): remove mobileanalytics as it's replaced by PinPoint
trivikr Nov 3, 2021
5b23b16
test(functional): move legacy fips tests to legacy folder
trivikr Nov 3, 2021
20ee75c
test(functional): fips+dualstack endpoints
trivikr Nov 3, 2021
2977852
test(functional): attempt to write single function for endpoints tests
trivikr Nov 3, 2021
91eb32c
test(functional): re-add psuedo region tests in fips-pseudo-region fo…
trivikr Nov 3, 2021
c66cf55
test(functional): add comment about pseudo regions tests
trivikr Nov 3, 2021
e990170
test(config-resolver): getHostnameFromVariants.spec.ts
trivikr Nov 3, 2021
24958e7
test(config-resolver): getRegionInfo.spec.ts
trivikr Nov 3, 2021
9b1c2bb
chore(config-resolver): throw error if hostnameresolution fails
trivikr Nov 3, 2021
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { EndpointVariant } from "./EndpointVariant";
import { getHostnameFromVariants, GetHostnameFromVariantsOptions } from "./getHostnameFromVariants";

describe(getHostnameFromVariants.name, () => {
const getMockHostname = (options: GetHostnameFromVariantsOptions) => JSON.stringify(options);
const getMockTags = ({ isFipsEndpoint, isDualstackEndpoint }: GetHostnameFromVariantsOptions) => [
...(isFipsEndpoint ? ["fips"] : []),
...(isDualstackEndpoint ? ["dualstack"] : []),
];
const getMockVariants = () =>
[
{ isFipsEndpoint: false, isDualstackEndpoint: false },
{ isFipsEndpoint: false, isDualstackEndpoint: true },
{ isFipsEndpoint: true, isDualstackEndpoint: false },
{ isFipsEndpoint: true, isDualstackEndpoint: true },
].map((options) => ({
hostname: getMockHostname(options),
tags: getMockTags(options),
}));

const testCases = [
[false, false],
[false, true],
[true, false],
[true, true],
];

describe("returns hostname if present in variants", () => {
it.each(testCases)("isFipsEndpoint: %s, isDualstackEndpoint: %s", (isFipsEndpoint, isDualstackEndpoint) => {
const options = { isFipsEndpoint, isDualstackEndpoint };
const variants = getMockVariants() as EndpointVariant[];
expect(getHostnameFromVariants(variants, options)).toEqual(getMockHostname(options));
});
});

describe("returns undefined if not present in variants", () => {
it.each(testCases)("isFipsEndpoint: %s, isDualstackEndpoint: %s", (isFipsEndpoint, isDualstackEndpoint) => {
const options = { isFipsEndpoint, isDualstackEndpoint };
const variants = getMockVariants() as EndpointVariant[];
expect(
getHostnameFromVariants(
variants.filter(({ tags }) => JSON.stringify(tags) !== JSON.stringify(getMockTags(options))),
options
)
).toBeUndefined();
});
});

describe("returns undefined if variants in undefined", () => {
it.each(testCases)("isFipsEndpoint: %s, isDualstackEndpoint: %s", (isFipsEndpoint, isDualstackEndpoint) => {
const options = { isFipsEndpoint, isDualstackEndpoint };
expect(getHostnameFromVariants(undefined, options)).toBeUndefined();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { EndpointVariant } from "./EndpointVariant";

export interface GetHostnameFromVariantsOptions {
isFipsEndpoint: boolean;
isDualstackEndpoint: boolean;
}

export const getHostnameFromVariants = (
variants: EndpointVariant[] = [],
{ isFipsEndpoint, isDualstackEndpoint }: GetHostnameFromVariantsOptions
) =>
variants.find(
({ tags }) => isFipsEndpoint === tags.includes("fips") && isDualstackEndpoint === tags.includes("dualstack")
)?.hostname;

This file was deleted.

This file was deleted.

56 changes: 44 additions & 12 deletions packages/config-resolver/src/regionInfo/getRegionInfo.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { getHostnameFromVariants } from "./getHostnameFromVariants";
import { getRegionInfo } from "./getRegionInfo";
import { getResolvedHostname } from "./getResolvedHostname";
import { getResolvedPartition } from "./getResolvedPartition";
import { getResolvedSigningRegion } from "./getResolvedSigningRegion";
import { PartitionHash } from "./PartitionHash";
import { RegionHash } from "./RegionHash";

jest.mock("./getHostnameFromVariants");
jest.mock("./getResolvedHostname");
jest.mock("./getResolvedPartition");
jest.mock("./getResolvedSigningRegion");
Expand Down Expand Up @@ -64,12 +66,14 @@ describe(getRegionInfo.name, () => {
});

beforeEach(() => {
(getHostnameFromVariants as jest.Mock).mockReturnValue(mockHostname);
(getResolvedHostname as jest.Mock).mockReturnValue(mockHostname);
(getResolvedPartition as jest.Mock).mockReturnValue(mockPartition);
(getResolvedSigningRegion as jest.Mock).mockReturnValue(undefined);
});

afterEach(() => {
expect(getHostnameFromVariants).toHaveBeenCalledTimes(2);
expect(getResolvedHostname).toHaveBeenCalledTimes(1);
expect(getResolvedPartition).toHaveBeenCalledTimes(1);
jest.clearAllMocks();
Expand All @@ -83,17 +87,22 @@ describe(getRegionInfo.name, () => {
const mockGetResolvedPartitionOptions = getMockResolvedPartitionOptions(mockPartitionHash);
const mockGetRegionInfoOptions = getMockRegionInfoOptions(mockRegionHash, mockGetResolvedPartitionOptions);

const mockResolvedRegion = getMockResolvedRegion(regionCase);
const mockRegionHostname = mockGetRegionInfoOptions.regionHash[mockResolvedRegion]?.hostname;
const mockPartitionHostname = mockGetRegionInfoOptions.partitionHash[mockPartition]?.hostname;

(getHostnameFromVariants as jest.Mock).mockReturnValueOnce(mockRegionHostname);
(getHostnameFromVariants as jest.Mock).mockReturnValueOnce(mockPartitionHostname);

expect(getRegionInfo(mockRegion, mockGetRegionInfoOptions)).toEqual({
signingService: mockSigningService,
hostname: mockHostname,
partition: mockPartition,
});

const mockResolvedRegion = getMockResolvedRegion(regionCase);
expect(getResolvedHostname).toHaveBeenCalledWith(mockResolvedRegion, {
signingService: mockSigningService,
regionHostname: mockGetRegionInfoOptions.regionHash[mockResolvedRegion]?.hostname,
partitionHostname: mockGetRegionInfoOptions.partitionHash[mockPartition]?.hostname,
regionHostname: mockRegionHostname,
partitionHostname: mockPartitionHostname,
});
expect(getResolvedPartition).toHaveBeenCalledWith(mockRegion, mockGetResolvedPartitionOptions);
expect(getResolvedSigningRegion).toHaveBeenCalledWith(mockRegion, {
Expand Down Expand Up @@ -133,6 +142,13 @@ describe(getRegionInfo.name, () => {
const mockGetResolvedPartitionOptions = getMockResolvedPartitionOptions(mockPartitionHash);
const mockGetRegionInfoOptions = getMockRegionInfoOptions(mockRegionHash, mockGetResolvedPartitionOptions);

const mockResolvedRegion = getMockResolvedRegion(regionCase);
const mockRegionHostname = mockGetRegionInfoOptions.regionHash[mockResolvedRegion]?.hostname;
const mockPartitionHostname = mockGetRegionInfoOptions.partitionHash[mockPartition]?.hostname;

(getHostnameFromVariants as jest.Mock).mockReturnValueOnce(mockRegionHostname);
(getHostnameFromVariants as jest.Mock).mockReturnValueOnce(mockPartitionHostname);

const mockRegionHashWithSigningRegion = getMockRegionHashWithSigningRegion(
regionCase,
mockRegionHash,
Expand All @@ -148,11 +164,9 @@ describe(getRegionInfo.name, () => {
signingRegion: mockSigningRegion,
});

const mockResolvedRegion = getMockResolvedRegion(regionCase);
expect(getResolvedHostname).toHaveBeenCalledWith(mockResolvedRegion, {
signingService: mockSigningService,
regionHostname: mockGetRegionInfoOptions.regionHash[mockResolvedRegion]?.hostname,
partitionHostname: mockGetRegionInfoOptions.partitionHash[mockPartition]?.hostname,
regionHostname: mockRegionHostname,
partitionHostname: mockPartitionHostname,
});
expect(getResolvedPartition).toHaveBeenCalledWith(mockRegion, mockGetResolvedPartitionOptions);
expect(getResolvedSigningRegion).toHaveBeenCalledWith(mockRegion, {
Expand Down Expand Up @@ -192,6 +206,13 @@ describe(getRegionInfo.name, () => {
const mockGetResolvedPartitionOptions = getMockResolvedPartitionOptions(mockPartitionHash);
const mockGetRegionInfoOptions = getMockRegionInfoOptions(mockRegionHash, mockGetResolvedPartitionOptions);

const mockResolvedRegion = getMockResolvedRegion(regionCase);
const mockRegionHostname = mockGetRegionInfoOptions.regionHash[mockResolvedRegion]?.hostname;
const mockPartitionHostname = mockGetRegionInfoOptions.partitionHash[mockPartition]?.hostname;

(getHostnameFromVariants as jest.Mock).mockReturnValueOnce(mockRegionHostname);
(getHostnameFromVariants as jest.Mock).mockReturnValueOnce(mockPartitionHostname);

const mockRegionHashWithSigningRegion = getMockRegionHashWithSigningService(
regionCase,
mockRegionHash,
Expand All @@ -206,11 +227,9 @@ describe(getRegionInfo.name, () => {
partition: mockPartition,
});

const mockResolvedRegion = getMockResolvedRegion(regionCase);
expect(getResolvedHostname).toHaveBeenCalledWith(mockResolvedRegion, {
signingService: mockSigningService,
regionHostname: mockGetRegionInfoOptions.regionHash[mockResolvedRegion]?.hostname,
partitionHostname: mockGetRegionInfoOptions.partitionHash[mockPartition]?.hostname,
regionHostname: mockRegionHostname,
partitionHostname: mockPartitionHostname,
});
expect(getResolvedPartition).toHaveBeenCalledWith(mockRegion, mockGetResolvedPartitionOptions);
expect(getResolvedSigningRegion).toHaveBeenCalledWith(mockRegion, {
Expand All @@ -219,4 +238,17 @@ describe(getRegionInfo.name, () => {
});
});
});

it("throws error if hostname is not defined", () => {
(getResolvedHostname as jest.Mock).mockReturnValueOnce(undefined);
const mockRegionHash = getMockRegionHash(RegionCase.REGION);
const mockPartitionHash = getMockPartitionHash(RegionCase.REGION);
expect(() => {
getRegionInfo(mockRegion, {
signingService: mockSigningService,
regionHash: mockRegionHash,
partitionHash: mockPartitionHash,
});
}).toThrow();
});
});
25 changes: 19 additions & 6 deletions packages/config-resolver/src/regionInfo/getRegionInfo.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,42 @@
import { RegionInfo } from "@aws-sdk/types";

import { getHostnameFromVariants } from "./getHostnameFromVariants";
import { getResolvedHostname } from "./getResolvedHostname";
import { getResolvedPartition } from "./getResolvedPartition";
import { getResolvedSigningRegion } from "./getResolvedSigningRegion";
import { PartitionHash } from "./PartitionHash";
import { RegionHash } from "./RegionHash";

export interface GetRegionInfoOptions {
isFipsEndpoint?: boolean;
isDualstackEndpoint?: boolean;
signingService: string;
regionHash: RegionHash;
partitionHash: PartitionHash;
}

export const getRegionInfo = (
region: string,
{ signingService, regionHash, partitionHash }: GetRegionInfoOptions
{
isFipsEndpoint = false,
isDualstackEndpoint = false,
signingService,
regionHash,
partitionHash,
}: GetRegionInfoOptions
): RegionInfo => {
const partition = getResolvedPartition(region, { partitionHash });
const resolvedRegion = region in regionHash ? region : partitionHash[partition]?.endpoint ?? region;

const hostname = getResolvedHostname(resolvedRegion, {
signingService,
regionHostname: regionHash[resolvedRegion]?.hostname,
partitionHostname: partitionHash[partition]?.hostname,
});
const hostnameOptions = { isFipsEndpoint, isDualstackEndpoint };
const regionHostname = getHostnameFromVariants(regionHash[resolvedRegion]?.variants, hostnameOptions);
const partitionHostname = getHostnameFromVariants(partitionHash[partition]?.variants, hostnameOptions);
const hostname = getResolvedHostname(resolvedRegion, { regionHostname, partitionHostname });

if (hostname === undefined) {
throw new Error(`Endpoint resolution failed for: ${{ resolvedRegion, isFipsEndpoint, isDualstackEndpoint }}`);
}

const signingRegion = getResolvedSigningRegion(region, {
hostname,
signingRegion: regionHash[resolvedRegion]?.signingRegion,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,30 @@
import { getHostnameTemplate } from "./getHostnameTemplate";
import { getResolvedHostname } from "./getResolvedHostname";

jest.mock("./getHostnameTemplate");

describe(getResolvedHostname.name, () => {
const mockSigningService = "mockSigningService";
const mockRegion = "mockRegion";
const mockHostname = "{region}.mockHostname.com";

afterEach(() => {
jest.clearAllMocks();
});

it("returns hostname if available in regionHash", () => {
it("returns hostname if available in regionHostname", () => {
expect(
getResolvedHostname(mockRegion, {
signingService: mockSigningService,
regionHostname: mockHostname,
})
).toBe(mockHostname);
expect(getHostnameTemplate).not.toHaveBeenCalled();
});

it("returns hostname from hostname template when not available in regionHash", () => {
(getHostnameTemplate as jest.Mock).mockReturnValue(mockHostname);

it("returns hostname from partitionHostname when not available in partitionHostname", () => {
expect(
getResolvedHostname(mockRegion, {
signingService: mockSigningService,
partitionHostname: mockHostname,
})
).toBe(mockHostname.replace("{region}", mockRegion));
});

expect(getHostnameTemplate).toHaveBeenCalledTimes(1);
expect(getHostnameTemplate).toHaveBeenCalledWith(mockSigningService, {
partitionHostname: mockHostname,
});
it("returns undefined not available in either regionHostname or partitionHostname", () => {
expect(getResolvedHostname(mockRegion, {})).toBeUndefined();
});
});
12 changes: 7 additions & 5 deletions packages/config-resolver/src/regionInfo/getResolvedHostname.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { getHostnameTemplate } from "./getHostnameTemplate";

export interface GetResolvedHostnameOptions {
signingService: string;
regionHostname?: string;
partitionHostname?: string;
}

export const getResolvedHostname = (
resolvedRegion: string,
{ signingService, regionHostname, partitionHostname }: GetResolvedHostnameOptions
) => regionHostname ?? getHostnameTemplate(signingService, { partitionHostname }).replace("{region}", resolvedRegion);
{ regionHostname, partitionHostname }: GetResolvedHostnameOptions
): string | undefined =>
regionHostname
? regionHostname
: partitionHostname
? partitionHostname.replace("{region}", resolvedRegion)
: undefined;
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const getClientPackageName = (sdkId: string) =>
.map((word) => word.toLowerCase())
.join("-")}`;

describe("endpoints.fips", () => {
// These tests should be removed when pseudo regions are deprecated.
describe("endpoints.fips-pseudo-region", () => {
for (const { sdkId, region, signingRegion, hostname } of testCases) {
const clientPackageName = getClientPackageName(sdkId);
it(`testing "${clientPackageName}" with region: ${region}`, async () => {
Expand Down
Loading