mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-09-10 22:14:48 -05:00
fix(rest-api): vm export correctly handle zstd and gzip compression (#9960)
This commit is contained in:
@@ -252,7 +252,7 @@ export interface Xapi {
|
||||
): Promise<void>
|
||||
VM_export(
|
||||
vmRef: XenApiVm['$ref'],
|
||||
opts?: { cancelToken?: unknown; compress?: boolean; useSnapshot?: boolean }
|
||||
opts?: { cancelToken?: unknown; compress?: boolean | 'zstd' | 'gzip'; useSnapshot?: boolean }
|
||||
): ReturnType<Xapi['getResource']>
|
||||
VM_import(
|
||||
stream: Readable,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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<XoVmSnapshot> {
|
||||
*/
|
||||
@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<XoVmSnapshot> {
|
||||
@Request() req: ExRequest,
|
||||
@Path() id: string,
|
||||
@Path() format: 'xva' | 'ova',
|
||||
@Query() compress?: boolean
|
||||
@Query() compress?: Parameters<VmService['export']>[2]['compress']
|
||||
): Promise<Readable> {
|
||||
const stream = await this.#vmService.export(id as XoVmSnapshot['id'], 'VM-snapshot', {
|
||||
compress,
|
||||
|
||||
@@ -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<XoVmTemplate> {
|
||||
*/
|
||||
@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<XoVmTemplate> {
|
||||
@Request() req: ExRequest,
|
||||
@Path() id: string,
|
||||
@Path() format: 'xva' | 'ova',
|
||||
@Query() compress?: boolean
|
||||
@Query() compress?: Parameters<VmService['export']>[2]['compress']
|
||||
): Promise<Readable> {
|
||||
const stream = await this.#vmService.export(id as XoVmTemplate['id'], 'VM-template', {
|
||||
compress,
|
||||
|
||||
@@ -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<XoVm> {
|
||||
*/
|
||||
@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<XoVm> {
|
||||
async exportVm(
|
||||
@Request() req: ExRequest,
|
||||
@Path() id: string,
|
||||
@Path() format: 'xva' | 'ova',
|
||||
@Query() compress?: boolean
|
||||
@Path() format: Parameters<VmService['export']>[2]['format'],
|
||||
@Query() compress?: Parameters<VmService['export']>[2]['compress']
|
||||
): Promise<Readable> {
|
||||
const stream = await this.#vmService.export(id as XoVm['id'], 'VM', { compress, format, response: req.res })
|
||||
process.on('SIGTERM', () => req.destroy())
|
||||
|
||||
@@ -180,7 +180,11 @@ export class VmService {
|
||||
async export<Vm extends XoVm | XoVmSnapshot | XoVmTemplate>(
|
||||
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<Readable> {
|
||||
const xapiVm = this.#restApi.getXapiObject(id, vmType)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user