Skip to content
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

feat: support region configuration option #5901

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions packages/zip-it-and-ship-it/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface ManifestFunction {
name: string
path: string
priority?: number
region?: string
routes?: ExtendedRoute[]
runtime: string
runtimeVersion?: string
Expand Down Expand Up @@ -62,6 +63,7 @@ const formatFunctionForManifest = ({
path,
priority,
trafficRules,
region,
routes,
runtime,
runtimeVersion,
Expand All @@ -79,6 +81,7 @@ const formatFunctionForManifest = ({
mainFile,
name,
priority,
region,
trafficRules,
runtimeVersion,
path: resolve(path),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getImports } from '../parser/imports.js'
import { safelyParseSource, safelyReadSource } from '../parser/index.js'
import type { ModuleFormat } from '../utils/module_format.js'

import { REGIONS } from './properties/region.js'
import { parse as parseSchedule } from './properties/schedule.js'

export const IN_SOURCE_CONFIG_MODULE = '@netlify/functions'
Expand All @@ -26,6 +27,7 @@ export interface StaticAnalysisResult {
excludedRoutes?: Route[]
inputModuleFormat?: ModuleFormat
invocationMode?: InvocationMode
region?: string
routes?: ExtendedRoute[]
runtimeAPIVersion?: number
}
Expand All @@ -37,6 +39,11 @@ interface FindISCDeclarationsOptions {
const httpMethod = z.enum(['GET', 'POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE', 'HEAD'])
const httpMethods = z.preprocess((input) => (typeof input === 'string' ? input.toUpperCase() : input), httpMethod)
const path = z.string().startsWith('/', { message: "Must start with a '/'" })
const region = z.enum(REGIONS, {
errorMap: () => ({
message: `Must be one of the supported regions (${REGIONS.join(', ')})`,
}),
})

export type HttpMethod = z.infer<typeof httpMethod>
export type HttpMethods = z.infer<typeof httpMethods>
Expand Down Expand Up @@ -70,6 +77,7 @@ export const inSourceConfig = functionConfig
.optional(),
preferStatic: z.boolean().optional().catch(undefined),
rateLimit: rateLimit.optional().catch(undefined),
region: region.optional(),
})

export type InSourceConfig = z.infer<typeof inSourceConfig>
Expand Down Expand Up @@ -149,6 +157,10 @@ export const parseSource = (source: string, { functionName }: FindISCDeclaration
methods: data.method ?? [],
prefer_static: data.preferStatic || undefined,
}))

if (data.region) {
result.region = data.region
}
} else {
// TODO: Handle multiple errors.
const [issue] = error.issues
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const REGIONS = [
'ap-northeast-1',
'ap-southeast-1',
'ap-southeast-2',
'ca-central-1',
'eu-central-1',
'eu-west-1',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to include the regions that we don't support in self-serve mode (docs)? The reason we wanted folks to go through Support is to assess whether customers really need these

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch. I copied this list from the UI but didn't realise two of the regions had a boolean set to false. I've removed them in 8546f9c.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, there's a third one. Fixed in 30be130.

'eu-west-2',
'sa-east-1',
'us-east-1',
'us-east-2',
'us-west-1',
'us-west-2',
] as const
2 changes: 2 additions & 0 deletions packages/zip-it-and-ship-it/src/utils/format_result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type FunctionResult = Omit<FunctionArchive, 'runtime'> & {
bootstrapVersion?: string
routes?: ExtendedRoute[]
excludedRoutes?: Route[]
region?: string
runtime: RuntimeName
schedule?: string
runtimeAPIVersion?: number
Expand All @@ -20,6 +21,7 @@ export const formatZipResult = (archive: FunctionArchive) => {
staticAnalysisResult: undefined,
routes: archive.staticAnalysisResult?.routes,
excludedRoutes: archive.staticAnalysisResult?.excludedRoutes,
region: archive.staticAnalysisResult?.region,
runtime: archive.runtime.name,
schedule: archive.staticAnalysisResult?.config?.schedule ?? archive?.config?.schedule,
runtimeAPIVersion: archive.staticAnalysisResult?.runtimeAPIVersion,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default async () =>
new Response('<h1>Hello world</h1>', {
headers: {
'content-type': 'text/html',
},
})

export const config = {
region: 'eu-west-1',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expect, test } from 'vitest'

import { parseSource } from '../../../../src/runtimes/node/in_source_config/index.js'
import { REGIONS } from '../../../../src/runtimes/node/in_source_config/properties/region.js'
import { getLogger } from '../../../../src/utils/logger.js'

describe('`schedule` helper', () => {
Expand Down Expand Up @@ -838,4 +839,60 @@ describe('V2 API', () => {
runtimeAPIVersion: 2,
})
})

describe('`region` property', () => {
test('With no region', () => {
const source = `
export default async () => new Response("Hello!")
export const config = {}`

const isc = parseSource(source, options)
expect(isc).toEqual({
config: {},
excludedRoutes: [],
inputModuleFormat: 'esm',
routes: [],
runtimeAPIVersion: 2,
})
})

test('With a supported region', () => {
const source = `
export default async () => new Response("Hello!")
export const config = { region: 'eu-west-1' }`

const isc = parseSource(source, options)
expect(isc).toEqual({
config: { region: 'eu-west-1' },
excludedRoutes: [],
inputModuleFormat: 'esm',
region: 'eu-west-1',
routes: [],
runtimeAPIVersion: 2,
})
})

test('With an unsupported region', () => {
expect.assertions(4)

const source = `
export default async () => new Response("Hello!")
export const config = { region: 'mars-west-1' }`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👽


try {
parseSource(source, options)
} catch (error) {
const { customErrorInfo, message } = error

expect(message).toBe(
`Function func1 has a configuration error on 'region': Must be one of the supported regions (${REGIONS.join(
', ',
)})`,
)
expect(customErrorInfo.type).toBe('functionsBundling')
expect(customErrorInfo.location.functionName).toBe('func1')
expect(customErrorInfo.location.runtime).toBe('js')
}
})
})
})
31 changes: 31 additions & 0 deletions packages/zip-it-and-ship-it/tests/v2api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -784,4 +784,35 @@ describe.runIf(semver.gte(nodeVersion, '18.13.0'))('V2 functions API', () => {
expect(manifest.functions[0].name).toBe('function')
expect(manifest.functions[0].buildData).toEqual({ bootstrapVersion, runtimeAPIVersion: 2 })
})

test('Adds the selected region to the manifest file', async () => {
const bootstrapPath = getBootstrapPath()
const bootstrapPackageJson = await readFile(resolve(bootstrapPath, '..', '..', 'package.json'), 'utf8')
const { version: bootstrapVersion } = JSON.parse(bootstrapPackageJson)

const { path: tmpDir } = await getTmpDir({ prefix: 'zip-it-test' })
const manifestPath = join(tmpDir, 'manifest.json')

const { files } = await zipFixture('v2-region', {
fixtureDir: FIXTURES_ESM_DIR,
opts: {
featureFlags: {
zisi_add_metadata_file: true,
},
manifest: manifestPath,
},
})

expect(files.length).toBe(1)
expect(files[0].name).toBe('function')
expect(files[0].bootstrapVersion).toBe(bootstrapVersion)
expect(files[0].runtimeAPIVersion).toBe(2)

const manifestString = await readFile(manifestPath, { encoding: 'utf8' })
const manifest = JSON.parse(manifestString)

expect(manifest.functions.length).toBe(1)
expect(manifest.functions[0].name).toBe('function')
expect(manifest.functions[0].buildData).toEqual({ bootstrapVersion, runtimeAPIVersion: 2 })
})
})
Loading