@xen-orchestra/backups 0.40.0 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,7 +21,12 @@ export class RestoreMetadataBackup {
21
21
  })
22
22
  } else {
23
23
  const metadata = JSON.parse(await handler.readFile(join(backupId, 'metadata.json')))
24
- return String(await handler.readFile(resolve(backupId, metadata.data ?? 'data.json')))
24
+ const dataFileName = resolve(backupId, metadata.data ?? 'data.json')
25
+ const data = await handler.readFile(dataFileName)
26
+
27
+ // if data is JSON, sent it as a plain string, otherwise, consider the data as binary and encode it
28
+ const isJson = dataFileName.endsWith('.json')
29
+ return isJson ? data.toString() : { encoding: 'base64', data: data.toString('base64') }
25
30
  }
26
31
  }
27
32
  }
@@ -22,7 +22,13 @@ export class XoMetadataBackup {
22
22
  const dir = `${scheduleDir}/${formatFilenameDate(timestamp)}`
23
23
 
24
24
  const data = job.xoMetadata
25
- const dataBaseName = './data.json'
25
+ let dataBaseName = './data'
26
+
27
+ // JSON data is sent as plain string, binary data is sent as an object with `data` and `encoding properties
28
+ const isJson = typeof data === 'string'
29
+ if (isJson) {
30
+ dataBaseName += '.json'
31
+ }
26
32
 
27
33
  const metadata = JSON.stringify(
28
34
  {
@@ -54,7 +60,7 @@ export class XoMetadataBackup {
54
60
  async () => {
55
61
  const handler = adapter.handler
56
62
  const dirMode = this._config.dirMode
57
- await handler.outputFile(dataFileName, data, { dirMode })
63
+ await handler.outputFile(dataFileName, isJson ? data : Buffer.from(data.data, data.encoding), { dirMode })
58
64
  await handler.outputFile(metaDataFileName, metadata, {
59
65
  dirMode,
60
66
  })
@@ -18,7 +18,7 @@ export const MixinXapiWriter = (BaseClass = Object) =>
18
18
  const vdiRefs = await xapi.VM_getDisks(baseVm.$ref)
19
19
  for (const vdiRef of vdiRefs) {
20
20
  const vdi = xapi.getObject(vdiRef)
21
- if (vdi.$SR.uuid !== this._heathCheckSr.uuid) {
21
+ if (vdi.$SR.uuid !== this._healthCheckSr.uuid) {
22
22
  return false
23
23
  }
24
24
  }
@@ -2,18 +2,21 @@
2
2
  // eslint-disable-next-line eslint-comments/disable-enable-pair
3
3
  /* eslint-disable n/shebang */
4
4
 
5
+ import { asyncEach } from '@vates/async-each'
5
6
  import { catchGlobalErrors } from '@xen-orchestra/log/configure'
6
7
  import { createLogger } from '@xen-orchestra/log'
7
8
  import { getSyncedHandler } from '@xen-orchestra/fs'
8
9
  import { join } from 'node:path'
10
+ import { load as loadConfig } from 'app-conf'
9
11
  import Disposable from 'promise-toolbox/Disposable'
10
- import min from 'lodash/min.js'
11
12
 
12
13
  import { getVmBackupDir } from '../_getVmBackupDir.mjs'
13
14
  import { RemoteAdapter } from '../RemoteAdapter.mjs'
14
15
 
15
16
  import { CLEAN_VM_QUEUE } from './index.mjs'
16
17
 
18
+ const APP_NAME = 'xo-merge-worker'
19
+ const APP_DIR = new URL('.', import.meta.url).pathname
17
20
  // -------------------------------------------------------------------
18
21
 
19
22
  catchGlobalErrors(createLogger('xo:backups:mergeWorker'))
@@ -34,6 +37,7 @@ const main = Disposable.wrap(async function* main(args) {
34
37
  for (let i = 0; i < 10; ++i) {
35
38
  const entries = await handler.list(CLEAN_VM_QUEUE)
36
39
  if (entries.length !== 0) {
40
+ entries.sort()
37
41
  return entries
38
42
  }
39
43
  await new Promise(timeoutResolver)
@@ -42,38 +46,47 @@ const main = Disposable.wrap(async function* main(args) {
42
46
 
43
47
  let taskFiles
44
48
  while ((taskFiles = await listRetry()) !== undefined) {
45
- const taskFileBasename = min(taskFiles)
46
- const previousTaskFile = join(CLEAN_VM_QUEUE, taskFileBasename)
47
- const taskFile = join(CLEAN_VM_QUEUE, '_' + taskFileBasename)
48
-
49
- // move this task to the end
50
- try {
51
- await handler.rename(previousTaskFile, taskFile)
52
- } catch (error) {
53
- // this error occurs if the task failed too many times (i.e. too many `_` prefixes)
54
- // there is nothing more that can be done
55
- if (error.code === 'ENAMETOOLONG') {
56
- await handler.unlink(previousTaskFile)
57
- }
58
-
59
- throw error
60
- }
49
+ const { concurrency } = await loadConfig(APP_NAME, {
50
+ appDir: APP_DIR,
51
+ ignoreUnknownFormats: true,
52
+ })
53
+ await asyncEach(
54
+ taskFiles,
55
+ async taskFileBasename => {
56
+ const previousTaskFile = join(CLEAN_VM_QUEUE, taskFileBasename)
57
+ const taskFile = join(CLEAN_VM_QUEUE, '_' + taskFileBasename)
58
+
59
+ // move this task to the end
60
+ try {
61
+ await handler.rename(previousTaskFile, taskFile)
62
+ } catch (error) {
63
+ // this error occurs if the task failed too many times (i.e. too many `_` prefixes)
64
+ // there is nothing more that can be done
65
+ if (error.code === 'ENAMETOOLONG') {
66
+ await handler.unlink(previousTaskFile)
67
+ }
61
68
 
62
- try {
63
- const vmDir = getVmBackupDir(String(await handler.readFile(taskFile)))
64
- try {
65
- await adapter.cleanVm(vmDir, { merge: true, logInfo: info, logWarn: warn, remove: true })
66
- } catch (error) {
67
- // consider the clean successful if the VM dir is missing
68
- if (error.code !== 'ENOENT') {
69
69
  throw error
70
70
  }
71
- }
72
71
 
73
- handler.unlink(taskFile).catch(error => warn('deleting task failure', { error }))
74
- } catch (error) {
75
- warn('failure handling task', { error })
76
- }
72
+ try {
73
+ const vmDir = getVmBackupDir(String(await handler.readFile(taskFile)))
74
+ try {
75
+ await adapter.cleanVm(vmDir, { merge: true, logInfo: info, logWarn: warn, remove: true })
76
+ } catch (error) {
77
+ // consider the clean successful if the VM dir is missing
78
+ if (error.code !== 'ENOENT') {
79
+ throw error
80
+ }
81
+ }
82
+
83
+ handler.unlink(taskFile).catch(error => warn('deleting task failure', { error }))
84
+ } catch (error) {
85
+ warn('failure handling task', { error })
86
+ }
87
+ },
88
+ { concurrency }
89
+ )
77
90
  }
78
91
  })
79
92
 
@@ -0,0 +1 @@
1
+ concurrency = 1
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/vatesfr/xen-orchestra.git"
10
10
  },
11
- "version": "0.40.0",
11
+ "version": "0.41.0",
12
12
  "engines": {
13
13
  "node": ">=14.18"
14
14
  },
@@ -17,6 +17,7 @@
17
17
  "test-integration": "node--test *.integ.mjs"
18
18
  },
19
19
  "dependencies": {
20
+ "@iarna/toml": "^2.2.5",
20
21
  "@kldzj/stream-throttle": "^1.1.1",
21
22
  "@vates/async-each": "^1.0.0",
22
23
  "@vates/cached-dns.lookup": "^1.0.0",
@@ -30,8 +31,9 @@
30
31
  "@xen-orchestra/fs": "^4.0.1",
31
32
  "@xen-orchestra/log": "^0.6.0",
32
33
  "@xen-orchestra/template": "^0.1.0",
33
- "compare-versions": "^5.0.1",
34
- "d3-time-format": "^3.0.0",
34
+ "app-conf": "^2.3.0",
35
+ "compare-versions": "^6.0.0",
36
+ "d3-time-format": "^4.1.0",
35
37
  "decorator-synchronized": "^0.6.0",
36
38
  "golike-defer": "^0.5.1",
37
39
  "limit-concurrency-decorator": "^0.5.0",
@@ -43,7 +45,7 @@
43
45
  "tar": "^6.1.15",
44
46
  "uuid": "^9.0.0",
45
47
  "vhd-lib": "^4.5.0",
46
- "xen-api": "^1.3.4",
48
+ "xen-api": "^1.3.5",
47
49
  "yazl": "^2.5.1"
48
50
  },
49
51
  "devDependencies": {
@@ -54,7 +56,7 @@
54
56
  "tmp": "^0.2.1"
55
57
  },
56
58
  "peerDependencies": {
57
- "@xen-orchestra/xapi": "^3.0.0"
59
+ "@xen-orchestra/xapi": "^3.0.1"
58
60
  },
59
61
  "license": "AGPL-3.0-or-later",
60
62
  "author": {