Skip to content

Commit f022e50

Browse files
committed
refactor(apps): retire network update jobs
1 parent d119673 commit f022e50

15 files changed

Lines changed: 35 additions & 63 deletions

File tree

apps/api/src/box/dto/job-type-map.dto.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ export interface JobTypeMap {
2525
[JobType.DESTROY_BOX]: {
2626
resourceType: [ResourceType.BOX]
2727
}
28-
[JobType.UPDATE_BOX_NETWORK_SETTINGS]: {
29-
resourceType: [ResourceType.BOX]
30-
}
3128
[JobType.EXPORT_BOX]: {
3229
resourceType: [ResourceType.BOX]
3330
}

apps/api/src/box/enums/job-type.enum.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ describe('JobType', () => {
1313
'START_BOX',
1414
'STOP_BOX',
1515
'DESTROY_BOX',
16-
'UPDATE_BOX_NETWORK_SETTINGS',
1716
'EXPORT_BOX',
1817
'IMPORT_BOX',
1918
'ROLLBACK_EXPORT_BOX',

apps/api/src/box/enums/job-type.enum.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ export enum JobType {
99
START_BOX = 'START_BOX',
1010
STOP_BOX = 'STOP_BOX',
1111
DESTROY_BOX = 'DESTROY_BOX',
12-
UPDATE_BOX_NETWORK_SETTINGS = 'UPDATE_BOX_NETWORK_SETTINGS',
1312
EXPORT_BOX = 'EXPORT_BOX',
1413
IMPORT_BOX = 'IMPORT_BOX',
1514
ROLLBACK_EXPORT_BOX = 'ROLLBACK_EXPORT_BOX',

apps/api/src/box/runner-adapter/runnerAdapter.v2.ts

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { JobStatus } from '../enums/job-status.enum'
1717
import { ResourceType } from '../enums/resource-type.enum'
1818
import { JobService } from '../services/job.service'
1919
import { BoxRepository } from '../repositories/box.repository'
20-
import { UpdateNetworkSettingsDTO } from '@boxlite-ai/runner-api-client'
2120

2221
/**
2322
* RunnerAdapterV2 implements RunnerAdapter for v2 runners.
@@ -184,26 +183,11 @@ export class RunnerAdapterV2 implements RunnerAdapter {
184183
}
185184

186185
async updateNetworkSettings(
187-
boxId: string,
188-
networkBlockAll?: boolean,
189-
networkAllowList?: string,
190-
networkLimitEgress?: boolean,
186+
_boxId: string,
187+
_networkBlockAll?: boolean,
188+
_networkAllowList?: string,
189+
_networkLimitEgress?: boolean,
191190
): Promise<void> {
192-
const payload: UpdateNetworkSettingsDTO = {
193-
networkBlockAll: networkBlockAll,
194-
networkAllowList: networkAllowList,
195-
networkLimitEgress: networkLimitEgress,
196-
}
197-
198-
await this.jobService.createJob(
199-
null,
200-
JobType.UPDATE_BOX_NETWORK_SETTINGS,
201-
this.runner.id,
202-
ResourceType.BOX,
203-
boxId,
204-
payload,
205-
)
206-
207-
this.logger.debug(`Created UPDATE_BOX_NETWORK_SETTINGS job for box ${boxId} on runner ${this.runner.id}`)
191+
throw new Error('Live network settings updates with runner API version 2 are not supported')
208192
}
209193
}

apps/api/src/box/services/box.service.spec.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ describe('BoxService network tunnel URLs', () => {
294294
describe('BoxService public defaults', () => {
295295
function makeCreateService() {
296296
const boxRepository = { insert: jest.fn(async (box: any) => box) } as any
297+
const warmPoolService = { fetchWarmPoolBox: jest.fn().mockResolvedValue(undefined) }
297298
const runner = { id: 'runner-1', draining: false, state: RunnerState.READY }
298299
const runnerService = {
299300
getRandomAvailableRunner: jest.fn().mockResolvedValue(runner),
@@ -315,15 +316,33 @@ describe('BoxService public defaults', () => {
315316
rollbackPendingUsage: jest.fn().mockResolvedValue(undefined),
316317
},
317318
redis: { exists: jest.fn().mockResolvedValue(1) },
319+
warmPoolService,
318320
runnerService,
319321
redisLockProvider,
320322
boxRepository,
321323
eventEmitter: { emitAsync: jest.fn().mockResolvedValue(undefined) },
322324
toBoxDto: jest.fn((box) => box),
323325
})
324-
return { service, boxRepository, runnerService, redisLockProvider }
326+
return { service, boxRepository, runnerService, redisLockProvider, warmPoolService }
325327
}
326328

329+
it.each([
330+
[{ networkBlockAll: true }, { boxLimitedNetworkEgress: false }, { networkBlockAll: true }],
331+
[{ networkAllowList: '10.0.0.0/8' }, { boxLimitedNetworkEgress: false }, { networkAllowList: '10.0.0.0/8' }],
332+
[{}, { boxLimitedNetworkEgress: true }, { networkBlockAll: true }],
333+
])('creates a fresh box instead of claiming a warm box when network policy is required', async (request, org, expected) => {
334+
const { service, boxRepository, warmPoolService } = makeCreateService()
335+
;(service as any).redis.exists.mockResolvedValue(0)
336+
337+
await service.create(
338+
{ name: 'restricted-box', image: 'base', ...request } as any,
339+
{ id: 'org-1', ...org } as any,
340+
)
341+
342+
expect(warmPoolService.fetchWarmPoolBox).not.toHaveBeenCalled()
343+
expect(boxRepository.insert).toHaveBeenCalledWith(expect.objectContaining(expected))
344+
})
345+
327346
it.each([
328347
[undefined, true],
329348
[false, false],

apps/api/src/box/services/box.service.ts

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ export class BoxService {
223223
// Restrict box creation to the supported pinned images; reject anything else
224224
// at the request boundary (defaults undefined -> base image).
225225
const image = assertSupportedImage(createBoxDto.image)
226+
const requiresFreshBoxForNetworkPolicy =
227+
createBoxDto.networkBlockAll !== undefined ||
228+
createBoxDto.networkAllowList !== undefined ||
229+
organization.boxLimitedNetworkEgress
226230

227231
this.organizationService.assertOrganizationIsNotSuspended(organization)
228232

@@ -237,7 +241,7 @@ export class BoxService {
237241
if (createBoxDto.volumes && createBoxDto.volumes.length > 0) {
238242
const volumeIdOrNames = createBoxDto.volumes.map((v) => v.volumeId)
239243
await this.volumeService.validateVolumes(organization.id, volumeIdOrNames)
240-
} else if (image) {
244+
} else if (image && !requiresFreshBoxForNetworkPolicy) {
241245
// No volumes requested — try to claim a pre-warmed box matching this image/spec
242246
// before creating a fresh one.
243247
const skipWarmPool = (await this.redis.exists(`warm-pool:skip:${image}`)) === 1
@@ -283,6 +287,8 @@ export class BoxService {
283287

284288
if (createBoxDto.networkBlockAll !== undefined) {
285289
box.networkBlockAll = createBoxDto.networkBlockAll
290+
} else if (organization.boxLimitedNetworkEgress) {
291+
box.networkBlockAll = true
286292
}
287293

288294
if (createBoxDto.networkAllowList !== undefined) {
@@ -375,21 +381,6 @@ export class BoxService {
375381
throw new BoxError('Runner not found for warm pool box')
376382
}
377383

378-
if (
379-
createBoxDto.networkBlockAll !== undefined ||
380-
createBoxDto.networkAllowList !== undefined ||
381-
organization.boxLimitedNetworkEgress
382-
) {
383-
const runner = await this.runnerService.findOneOrFail(warmPoolBox.runnerId)
384-
const runnerAdapter = await this.runnerAdapterFactory.create(runner)
385-
await runnerAdapter.updateNetworkSettings(
386-
warmPoolBox.id,
387-
createBoxDto.networkBlockAll,
388-
createBoxDto.networkAllowList,
389-
organization.boxLimitedNetworkEgress,
390-
)
391-
}
392-
393384
// Resolve the name at persist time. A caller-provided name updates in one
394385
// shot (reusing the pre-fetched entity). A generated default falls back to
395386
// "{name}-{boxId}" on collision and omits `entity` so each attempt re-reads

apps/api/src/migrations/pre-deploy/1786300000000-retire-unused-job-types-migration.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ describe('RetireUnusedJobTypes1786300000000', () => {
1414
expect(query.mock.calls[0][0]).toContain(`WHERE "state" = 'resizing'`)
1515
expect(query.mock.calls[0][0]).toContain(`"job"."type" = 'RESIZE_BOX'`)
1616
expect(query.mock.calls[1][0]).toContain(`UPDATE "job" SET "status" = 'FAILED'`)
17+
expect(query.mock.calls[1][0]).toContain(`'UPDATE_BOX_NETWORK_SETTINGS'`)
1718
})
1819
})

apps/api/src/migrations/pre-deploy/1786300000000-retire-unused-job-types-migration.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class RetireUnusedJobTypes1786300000000 implements MigrationInterface {
1313
`UPDATE "box" SET "state" = CASE "desiredState" WHEN 'started' THEN 'started'::"public"."box_state_enum" WHEN 'stopped' THEN 'stopped'::"public"."box_state_enum" ELSE 'error'::"public"."box_state_enum" END, "pending" = false, "errorReason" = CASE WHEN "desiredState" IN ('started', 'stopped') THEN "errorReason" ELSE 'Legacy resize job had an invalid desired state' END, "updatedAt" = NOW() WHERE "state" = 'resizing' AND EXISTS (SELECT 1 FROM "job" WHERE "job"."resourceId" = "box"."id" AND "job"."type" = 'RESIZE_BOX' AND "job"."completedAt" IS NULL)`,
1414
)
1515
await queryRunner.query(
16-
`UPDATE "job" SET "status" = 'FAILED', "completedAt" = NOW(), "updatedAt" = NOW(), "errorMessage" = 'Job type is no longer supported' WHERE "completedAt" IS NULL AND "type" IN ('RESIZE_BOX', 'CREATE_BACKUP', 'PULL_ARTIFACT', 'RECOVER_BOX', 'INSPECT_ARTIFACT_IN_REGISTRY', 'REMOVE_ARTIFACT')`,
16+
`UPDATE "job" SET "status" = 'FAILED', "completedAt" = NOW(), "updatedAt" = NOW(), "errorMessage" = 'Job type is no longer supported' WHERE "completedAt" IS NULL AND "type" IN ('RESIZE_BOX', 'CREATE_BACKUP', 'PULL_ARTIFACT', 'RECOVER_BOX', 'INSPECT_ARTIFACT_IN_REGISTRY', 'REMOVE_ARTIFACT', 'UPDATE_BOX_NETWORK_SETTINGS')`,
1717
)
1818
}
1919

apps/libs/api-client-go/api/openapi.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6181,7 +6181,6 @@ components:
61816181
- START_BOX
61826182
- STOP_BOX
61836183
- DESTROY_BOX
6184-
- UPDATE_BOX_NETWORK_SETTINGS
61856184
- EXPORT_BOX
61866185
- IMPORT_BOX
61876186
- ROLLBACK_EXPORT_BOX

apps/libs/api-client-go/model_job_type.go

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)