diff --git a/@vates/types/src/lib/xen-orchestra-xapi.mts b/@vates/types/src/lib/xen-orchestra-xapi.mts index 8eb3b7c884..ea22f4879b 100644 --- a/@vates/types/src/lib/xen-orchestra-xapi.mts +++ b/@vates/types/src/lib/xen-orchestra-xapi.mts @@ -252,7 +252,7 @@ export interface Xapi { ): Promise VM_export( vmRef: XenApiVm['$ref'], - opts?: { cancelToken?: unknown; compress?: boolean; useSnapshot?: boolean } + opts?: { cancelToken?: unknown; compress?: boolean | 'zstd' | 'gzip'; useSnapshot?: boolean } ): ReturnType VM_import( stream: Readable, diff --git a/@xen-orchestra/rest-api/src/middlewares/deprecated.middleware.mts b/@xen-orchestra/rest-api/src/middlewares/deprecated.middleware.mts new file mode 100644 index 0000000000..e2d001f7ca --- /dev/null +++ b/@xen-orchestra/rest-api/src/middlewares/deprecated.middleware.mts @@ -0,0 +1,14 @@ +import { createLogger } from '@xen-orchestra/log' +import type { NextFunction, Request, Response } from 'express' + +const log = createLogger('xo:rest-api:deprecated.middleware') + +export function vmExportCompressDeprecated(req: Request, _res: Response, next: NextFunction) { + const compress = req.query.compress + if (compress === 'true' || compress === 'false') { + log.warn("the query param 'compress' as boolean is deprecated. Please use an explicit value next time") + req.query.compress = compress === 'true' ? 'gzip' : undefined + } + + next() +} diff --git a/@xen-orchestra/rest-api/src/vm-snapshots/vm-snapshot.controller.mts b/@xen-orchestra/rest-api/src/vm-snapshots/vm-snapshot.controller.mts index 5459ad4f96..8fa90fda0a 100644 --- a/@xen-orchestra/rest-api/src/vm-snapshots/vm-snapshot.controller.mts +++ b/@xen-orchestra/rest-api/src/vm-snapshots/vm-snapshot.controller.mts @@ -45,6 +45,7 @@ import { } from '../open-api/oa-examples/vm-snapshot.oa-example.mjs' import { VmService } from '../vms/vm.service.mjs' import { messageIds, partialMessages } from '../open-api/oa-examples/message.oa-example.mjs' +import { vmExportCompressDeprecated } from '../middlewares/deprecated.middleware.mjs' @Route('vm-snapshots') @Security('*') @@ -104,7 +105,7 @@ export class VmSnapshotController extends XapiXoController { */ @Extension('x-mcp-exposure', 'deny') @Get('{id}.{format}') - @Middlewares(acl({ resource: 'vm-snapshot', action: 'export', objectId: 'params.id' })) + @Middlewares([acl({ resource: 'vm-snapshot', action: 'export', objectId: 'params.id' }), vmExportCompressDeprecated]) @SuccessResponse(200, 'Download started', 'application/octet-stream') @Response(forbiddenOperationResp.status, forbiddenOperationResp.description) @Response(notFoundResp.status, notFoundResp.description) @@ -113,7 +114,7 @@ export class VmSnapshotController extends XapiXoController { @Request() req: ExRequest, @Path() id: string, @Path() format: 'xva' | 'ova', - @Query() compress?: boolean + @Query() compress?: Parameters[2]['compress'] ): Promise { const stream = await this.#vmService.export(id as XoVmSnapshot['id'], 'VM-snapshot', { compress, diff --git a/@xen-orchestra/rest-api/src/vm-templates/vm-template.controller.mts b/@xen-orchestra/rest-api/src/vm-templates/vm-template.controller.mts index 5874ffd8ea..3b1a6250c6 100644 --- a/@xen-orchestra/rest-api/src/vm-templates/vm-template.controller.mts +++ b/@xen-orchestra/rest-api/src/vm-templates/vm-template.controller.mts @@ -46,6 +46,7 @@ import { import { VmService } from '../vms/vm.service.mjs' import { messageIds, partialMessages } from '../open-api/oa-examples/message.oa-example.mjs' import { partialTasks, taskIds } from '../open-api/oa-examples/task.oa-example.mjs' +import { vmExportCompressDeprecated } from '../middlewares/deprecated.middleware.mjs' @Route('vm-templates') @Security('*') @@ -105,7 +106,7 @@ export class VmTemplateController extends XapiXoController { */ @Extension('x-mcp-exposure', 'deny') @Get('{id}.{format}') - @Middlewares(acl({ resource: 'vm-template', action: 'export', objectId: 'params.id' })) + @Middlewares([acl({ resource: 'vm-template', action: 'export', objectId: 'params.id' }), vmExportCompressDeprecated]) @SuccessResponse(200, 'Download started', 'application/octet-stream') @Response(forbiddenOperationResp.status, forbiddenOperationResp.description) @Response(notFoundResp.status, notFoundResp.description) @@ -114,7 +115,7 @@ export class VmTemplateController extends XapiXoController { @Request() req: ExRequest, @Path() id: string, @Path() format: 'xva' | 'ova', - @Query() compress?: boolean + @Query() compress?: Parameters[2]['compress'] ): Promise { const stream = await this.#vmService.export(id as XoVmTemplate['id'], 'VM-template', { compress, diff --git a/@xen-orchestra/rest-api/src/vms/vm.controller.mts b/@xen-orchestra/rest-api/src/vms/vm.controller.mts index 88ca4dfd86..6a77bab078 100644 --- a/@xen-orchestra/rest-api/src/vms/vm.controller.mts +++ b/@xen-orchestra/rest-api/src/vms/vm.controller.mts @@ -35,7 +35,6 @@ import type { XoMessage, XoNetwork, XoVif, - XoPif, XoSr, } from '@vates/types' import { PassThrough, Readable } from 'node:stream' @@ -72,6 +71,7 @@ import { messageIds, partialMessages } from '../open-api/oa-examples/message.oa- import type { UnbrandedVmDashboard, UpdateVmRequestBody } from './vm.type.mjs' import type { CreateActionReturnType } from '../abstract-classes/base-controller.mjs' import { Task } from '@vates/task' +import { vmExportCompressDeprecated } from '../middlewares/deprecated.middleware.mjs' const IGNORED_VDIS_TAG = '[NOSNAP]' @@ -141,7 +141,7 @@ export class VmController extends XapiXoController { */ @Extension('x-mcp-exposure', 'deny') @Get('{id}.{format}') - @Middlewares(acl({ resource: 'vm', action: 'export', objectId: 'params.id' })) + @Middlewares([acl({ resource: 'vm', action: 'export', objectId: 'params.id' }), vmExportCompressDeprecated]) @SuccessResponse(200, 'Download started', 'application/octet-stream') @Response(forbiddenOperationResp.status, forbiddenOperationResp.description) @Response(notFoundResp.status, notFoundResp.description) @@ -149,8 +149,8 @@ export class VmController extends XapiXoController { async exportVm( @Request() req: ExRequest, @Path() id: string, - @Path() format: 'xva' | 'ova', - @Query() compress?: boolean + @Path() format: Parameters[2]['format'], + @Query() compress?: Parameters[2]['compress'] ): Promise { const stream = await this.#vmService.export(id as XoVm['id'], 'VM', { compress, format, response: req.res }) process.on('SIGTERM', () => req.destroy()) diff --git a/@xen-orchestra/rest-api/src/vms/vm.service.mts b/@xen-orchestra/rest-api/src/vms/vm.service.mts index 8b46b67b80..49c3b15aab 100644 --- a/@xen-orchestra/rest-api/src/vms/vm.service.mts +++ b/@xen-orchestra/rest-api/src/vms/vm.service.mts @@ -180,7 +180,11 @@ export class VmService { async export( id: Vm['id'], vmType: Vm['type'], - { compress, format, response }: { compress?: boolean; format: 'ova' | 'xva'; response?: ExResponse } + { + compress, + format, + response, + }: { compress?: boolean | 'zstd' | 'gzip'; format: 'ova' | 'xva'; response?: ExResponse } ): Promise { const xapiVm = this.#restApi.getXapiObject(id, vmType) diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 3aa022f79b..08f574ca4c 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -10,6 +10,7 @@ ### Enhancements > Users must be able to say: "Nice enhancement, I'm eager to test it" + - [IPMI-Plugin] Add default outlet regex to the dell preset (PR [#9884](https://github.com/vatesfr/xen-orchestra/pull/9884)) - [REST API] Expose `POST /backup-repositories` and `PATCH /backup-repositories/:id` REST routes (PR [#9852](https://github.com/vatesfr/xen-orchestra/pull/9852)) @@ -54,6 +55,7 @@ - xo-server-sdn-controller: apply/clean network rules on VIF update (PR [#9933](https://github.com/vatesfr/xen-orchestra/pull/9933)) - [Rest Api] Fix `possibly unhandled rejection invalid crendentials` (PR [#9938](https://github.com/vatesfr/xen-orchestra/pull/9938)) - [Backups] Fixed "Cannot read properties of undefined" issues (PR [#9944](https://github.com/vatesfr/xen-orchestra/pull/9944)) +- [REST API] `GET /vms/:id.:format`, `GET /vm-templates/:id.:format`, `GET /vm-snapshots/:id.:format` now correctly support explicit compress query param (`zstd` | `gzip`). Still support `true` | `false` as deprecated value (PR [#9960](https://github.com/vatesfr/xen-orchestra/pull/9960)) ### Packages to release