Skip to content
Merged
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
8 changes: 8 additions & 0 deletions src/template-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type ManifestService = {
port?: number
healthcheck?: string
volume?: boolean // needs a /data disk; the platform owns the size
alwaysOn?: boolean // idle mode; undeclared = the platform default (always-on for compute)
env?: ManifestEnv
}

Expand Down Expand Up @@ -156,6 +157,13 @@ export function validateManifest(m: TemplateManifest): string[] {
if (svc.port !== undefined && (!Number.isInteger(svc.port) || svc.port < 1 || svc.port > 65535)) {
problems.push(`${where}: port must be an integer between 1 and 65535, got: ${svc.port}`)
}
// A worker is PORTLESS: the platform deploys it as its own port-0 service, so nothing is routed
// to it and nothing probes it. The server's three refusals, said here before the upload.
if (svc.type === 'worker') {
if (svc.port !== undefined) problems.push(`${where}.port: a worker has no routed port — remove it (a worker is portless; declare type: web to serve HTTP)`)
if (svc.healthcheck !== undefined) problems.push(`${where}.healthcheck: a worker has no HTTP endpoint to probe — its health is the machine's state; remove it (or declare type: web)`)
if (svc.alwaysOn === false) problems.push(`${where}.alwaysOn: a worker cannot scale to zero — nothing is routed to it, so nothing would wake it; remove alwaysOn or set it true`)
}
if (svc.type === 'web' && !svc.healthcheck) problems.push(`${where}: web services must declare a healthcheck path`)
if (svc.healthcheck && !String(svc.healthcheck).startsWith('/')) problems.push(`${where}: healthcheck must be an absolute path (start with /)`)
// Sizing is the platform's, capped for the org's plan. Same answers the
Expand Down
12 changes: 12 additions & 0 deletions test/template.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,18 @@ describe('validateManifest', () => {
// The shape a manifest authors today passes.
expect(validateManifest({ code: 'x', version: '1', services: { a: { type: 'worker', image: 'a:1', volume: true } } })).toEqual([])
})
// A worker is PORTLESS (insta-platform#490): the executor deploys it as the platform's port-0
// service, so a port, a healthcheck or scale-to-zero on one is a contradiction the author hears
// here, before the upload. Mirrors templateManifest.ts.
it('a worker is portless: port, healthcheck and alwaysOn:false are refused, the bare shape passes', () => {
const worker = (extra: Record<string, unknown>) =>
({ code: 'x', version: '1', services: { bg: { type: 'worker', image: 'a:1', ...extra } } }) as unknown as TemplateManifest
expect(validateManifest(worker({}))).toEqual([])
expect(validateManifest(worker({ alwaysOn: true }))).toEqual([])
expect(validateManifest(worker({ port: 8080 })).join('\n')).toMatch(/a worker has no routed port/)
expect(validateManifest(worker({ healthcheck: '/' })).join('\n')).toMatch(/a worker has no HTTP endpoint/)
expect(validateManifest(worker({ alwaysOn: false })).join('\n')).toMatch(/a worker cannot scale to zero/)
})
})

describe('parseManifestYaml', () => {
Expand Down
Loading