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
21 changes: 20 additions & 1 deletion cli/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,23 @@ import { detectWorkflow } from '../lib/workflow.js'

export const options = { command: 'deploy', strict: true }

// Docker build args for a deploy. Query-string skew protection needs ONE value
// in three places: baked into the client assets as `?dpl=<id>` at build time,
// set as plt.dev/version on the workload, and therefore used as the gateway's
// match key. --version is that value, so it has to reach the build as well as
// the deploy.
//
// Exported so this is covered by a test: the failure is silent. Without the
// build arg the image builds, the workload deploys, and the app runs, but its
// assets carry no ?dpl, so ICC sees a version that was not built with its own id
// and correctly refuses to route to it. Nothing errors.
//
// An app whose Dockerfile does not declare `ARG PLT_DEPLOYMENT_ID=` simply
// ignores it.
export function deployBuildArgs (version) {
return version ? { PLT_DEPLOYMENT_ID: version } : {}
}

export function imageName (image) {
return image.split('/').at(-1).split('@')[0].split(':')[0]
}
Expand Down Expand Up @@ -91,7 +108,9 @@ export default async function cli (argv) {
}

const isWorkflow = detectWorkflow(dockerfile, envVars)
if (directory) await registry.buildFromDirectory(directory, appImage, { npmrc: args.npmrc })

const buildArgs = deployBuildArgs(args.version)
if (directory) await registry.buildFromDirectory(directory, appImage, { npmrc: args.npmrc, buildArgs })

const clusterStatus = await getClusterStatus({ context })
if (clusterStatus.kafka?.connectionString) {
Expand Down
1 change: 1 addition & 0 deletions profiles/development.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ platformatic:
check_interval_ms: 10000 # 10 seconds
traffic_window_ms: 60000 # 1 min
cookie_max_age: 43200 # 12h in seconds (keep default)
default_routing_mode: query # query | cookie; per-app override in ICC settings
icc_jobs:
enable: true

Expand Down
1 change: 1 addition & 0 deletions profiles/oss.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ platformatic:
check_interval_ms: 10000 # 10 seconds
traffic_window_ms: 30000 # 30 seconds for e2e testing
cookie_max_age: 43200 # 12h in seconds (keep default)
default_routing_mode: query # query | cookie; per-app override in ICC settings
icc_jobs:
enable: true

Expand Down
1 change: 1 addition & 0 deletions profiles/skew-protection.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ platformatic:
check_interval_ms: 10000 # 10 seconds
traffic_window_ms: 60000 # 1 min
cookie_max_age: 43200 # 12h in seconds (keep default)
default_routing_mode: query # query | cookie; per-app override in ICC settings
icc_jobs:
enable: true

Expand Down
20 changes: 19 additions & 1 deletion tests/deploy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { mkdtemp, readFile, rm } from 'node:fs/promises'
import { join } from 'node:path'
import { tmpdir } from 'node:os'
import { createDeployment } from '../lib/deploy.js'
import { imageName } from '../cli/deploy.js'
import { imageName, deployBuildArgs } from '../cli/deploy.js'

test('imageName handles registry ports, tags, and digests', () => {
assert.equal(imageName('localhost:5000/orders:v2'), 'orders')
Expand Down Expand Up @@ -49,3 +49,21 @@ test('workflow deployment has authoritative world metadata', async () => {
await rm(runDir, { recursive: true, force: true })
}
})

test('--version reaches the image build as PLT_DEPLOYMENT_ID', () => {
// The value has to land in three places at once: the build arg (so the client
// assets carry ?dpl=<id>), the plt.dev/version label, and therefore the
// gateway's match key. This covers the build-arg half; the label half is
// asserted by the createDeployment tests above.
assert.deepEqual(deployBuildArgs('v2'), { PLT_DEPLOYMENT_ID: 'v2' })
assert.deepEqual(deployBuildArgs('a1b2c3d4e5f6'), { PLT_DEPLOYMENT_ID: 'a1b2c3d4e5f6' })
})

test('a deploy without --version passes no build args', () => {
// Unversioned deploys must build exactly as before. Passing an empty
// PLT_DEPLOYMENT_ID would make the framework hooks stamp `?dpl=` on every
// asset URL, which matches nothing.
assert.deepEqual(deployBuildArgs(undefined), {})
assert.deepEqual(deployBuildArgs(null), {})
assert.deepEqual(deployBuildArgs(''), {})
})