feat(api-errors): throw custom errors when XAPI error is caught (#440)
See vatesfr/xo-web#1717
This commit is contained in:
parent
bc5b00781b
commit
2be7388696
@ -104,6 +104,7 @@
|
||||
"xml2js": "~0.4.6",
|
||||
"xo-acl-resolver": "^0.2.2",
|
||||
"xo-collection": "^0.4.0",
|
||||
"xo-common": "0.0.0",
|
||||
"xo-remote-parser": "^0.3",
|
||||
"xo-vmdk-to-vhd": "0.0.12"
|
||||
},
|
||||
|
@ -1,70 +0,0 @@
|
||||
import {JsonRpcError} from 'json-rpc-peer'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
// Export standard JSON-RPC errors.
|
||||
export { // eslint-disable-line no-duplicate-imports
|
||||
InvalidJson,
|
||||
InvalidParameters,
|
||||
InvalidRequest,
|
||||
JsonRpcError,
|
||||
MethodNotFound
|
||||
} from 'json-rpc-peer'
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export class NotImplemented extends JsonRpcError {
|
||||
constructor () {
|
||||
super('not implemented', 0)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export class NoSuchObject extends JsonRpcError {
|
||||
constructor (id, type) {
|
||||
super('no such object', 1, {id, type})
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export class Unauthorized extends JsonRpcError {
|
||||
constructor () {
|
||||
super('not authenticated or not enough permissions', 2)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export class InvalidCredential extends JsonRpcError {
|
||||
constructor () {
|
||||
super('invalid credential', 3)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export class AlreadyAuthenticated extends JsonRpcError {
|
||||
constructor () {
|
||||
super('already authenticated', 4)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export class ForbiddenOperation extends JsonRpcError {
|
||||
constructor (operation, reason) {
|
||||
super(`forbidden operation: ${operation}`, 5, reason)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// To be used with a user-readable message.
|
||||
// The message can be destined to be displayed to the front-end user.
|
||||
export class GenericError extends JsonRpcError {
|
||||
constructor (message) {
|
||||
super(message, 6)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { Unauthorized } from '../api-errors'
|
||||
import { unauthorized } from 'xo-common/api-errors'
|
||||
|
||||
export function create (props) {
|
||||
return this.createIpPool(props)
|
||||
@ -21,7 +21,7 @@ export function getAll (params) {
|
||||
const { user } = this
|
||||
|
||||
if (!user) {
|
||||
throw new Unauthorized()
|
||||
throw unauthorized()
|
||||
}
|
||||
|
||||
return this.getAllIpPools(user.permission === 'admin'
|
||||
|
@ -1,5 +1,3 @@
|
||||
import {GenericError} from '../api-errors'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export async function set ({
|
||||
@ -121,12 +119,7 @@ export {uploadPatch as patch}
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export async function mergeInto ({ source, target, force }) {
|
||||
try {
|
||||
await this.mergeXenPools(source._xapiId, target._xapiId, force)
|
||||
} catch (e) {
|
||||
// FIXME: should we expose plain XAPI error messages?
|
||||
throw new GenericError(e.message)
|
||||
}
|
||||
await this.mergeXenPools(source._xapiId, target._xapiId, force)
|
||||
}
|
||||
|
||||
mergeInto.params = {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {
|
||||
Unauthorized
|
||||
} from '../api-errors'
|
||||
unauthorized
|
||||
} from 'xo-common/api-errors'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@ -117,7 +117,7 @@ get.params = {
|
||||
export async function getAll () {
|
||||
const { user } = this
|
||||
if (!user) {
|
||||
throw new Unauthorized()
|
||||
throw unauthorized()
|
||||
}
|
||||
|
||||
return this.getAllResourceSets(user.id)
|
||||
|
@ -1,18 +1,14 @@
|
||||
import {deprecate} from 'util'
|
||||
|
||||
import { getUserPublicProperties } from '../utils'
|
||||
import {InvalidCredential, AlreadyAuthenticated} from '../api-errors'
|
||||
import {invalidCredentials} from 'xo-common/api-errors'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export async function signIn (credentials) {
|
||||
if (this.session.has('user_id')) {
|
||||
throw new AlreadyAuthenticated()
|
||||
}
|
||||
|
||||
const user = await this.authenticateUser(credentials)
|
||||
if (!user) {
|
||||
throw new InvalidCredential()
|
||||
throw invalidCredentials()
|
||||
}
|
||||
this.session.set('user_id', user.id)
|
||||
|
||||
|
@ -2,7 +2,7 @@ import forEach from 'lodash/forEach'
|
||||
import getKeys from 'lodash/keys'
|
||||
import moment from 'moment-timezone'
|
||||
|
||||
import { NoSuchObject } from '../api-errors'
|
||||
import { noSuchObject } from 'xo-common/api-errors'
|
||||
import { version as xoServerVersion } from '../../package.json'
|
||||
|
||||
// ===================================================================
|
||||
@ -50,7 +50,7 @@ export function methodSignature ({method: name}) {
|
||||
const method = this.apiMethods[name]
|
||||
|
||||
if (!method) {
|
||||
throw new NoSuchObject()
|
||||
throw noSuchObject()
|
||||
}
|
||||
|
||||
// Return an array for compatibility with XML-RPC.
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {InvalidParameters} from '../api-errors'
|
||||
import {invalidParameters} from 'xo-common/api-errors'
|
||||
import { getUserPublicProperties, mapToArray } from '../utils'
|
||||
|
||||
// ===================================================================
|
||||
@ -22,7 +22,7 @@ create.params = {
|
||||
// Deletes an existing user.
|
||||
async function delete_ ({id}) {
|
||||
if (id === this.session.get('user_id')) {
|
||||
throw new InvalidParameters('a user cannot delete itself')
|
||||
throw invalidParameters('a user cannot delete itself')
|
||||
}
|
||||
|
||||
await this.deleteUser(id)
|
||||
@ -61,10 +61,10 @@ export async function set ({id, email, password, permission, preferences}) {
|
||||
const isAdmin = this.user && this.user.permission === 'admin'
|
||||
if (isAdmin) {
|
||||
if (permission && id === this.session.get('user_id')) {
|
||||
throw new InvalidParameters('a user cannot change its own permission')
|
||||
throw invalidParameters('a user cannot change its own permission')
|
||||
}
|
||||
} else if (email || password || permission) {
|
||||
throw new InvalidParameters('this properties can only changed by an administrator')
|
||||
throw invalidParameters('this properties can only changed by an administrator')
|
||||
}
|
||||
|
||||
await this.updateUser(id, {email, password, permission, preferences})
|
||||
|
@ -3,9 +3,9 @@
|
||||
{coroutine: $coroutine} = require 'bluebird'
|
||||
|
||||
{format} = require 'json-rpc-peer'
|
||||
{InvalidParameters} = require '../api-errors'
|
||||
{invalidParameters} = require 'xo-common/api-errors'
|
||||
{isArray: $isArray, parseSize} = require '../utils'
|
||||
{JsonRpcError} = require '../api-errors'
|
||||
{JsonRpcError} = require 'json-rpc-peer'
|
||||
|
||||
#=====================================================================
|
||||
|
||||
@ -38,7 +38,7 @@ set = $coroutine (params) ->
|
||||
size = parseSize(params.size)
|
||||
|
||||
if size < vdi.size
|
||||
throw new InvalidParameters(
|
||||
throw invalidParameters(
|
||||
"cannot set new size (#{size}) below the current size (#{vdi.size})"
|
||||
)
|
||||
yield xapi.resizeVdi(ref, size)
|
||||
|
@ -13,10 +13,10 @@ startsWith = require 'lodash/startsWith'
|
||||
{format} = require 'json-rpc-peer'
|
||||
|
||||
{
|
||||
GenericError,
|
||||
InvalidParameters,
|
||||
Unauthorized
|
||||
} = require('../api-errors')
|
||||
forbiddenOperation,
|
||||
invalidParameters,
|
||||
unauthorized
|
||||
} = require('xo-common/api-errors')
|
||||
{
|
||||
forEach,
|
||||
formatXml: $js2xml,
|
||||
@ -49,7 +49,7 @@ checkPermissionOnSrs = (vm, permission = 'operate') -> (
|
||||
)
|
||||
|
||||
return @hasPermissions(@session.get('user_id'), permissions).then((success) => (
|
||||
throw new Unauthorized() unless success
|
||||
throw unauthorized() unless success
|
||||
))
|
||||
)
|
||||
|
||||
@ -65,7 +65,7 @@ create = $coroutine (params) ->
|
||||
{ user } = this
|
||||
resourceSet = extract(params, 'resourceSet')
|
||||
if not resourceSet and user.permission isnt 'admin'
|
||||
throw new Unauthorized()
|
||||
throw unauthorized()
|
||||
|
||||
template = extract(params, 'template')
|
||||
params.template = template._xapiId
|
||||
@ -402,7 +402,7 @@ migrate = $coroutine ({
|
||||
])
|
||||
|
||||
unless yield @hasPermissions(@session.get('user_id'), permissions)
|
||||
throw new Unauthorized()
|
||||
throw unauthorized()
|
||||
|
||||
yield @getXapi(vm).migrateVm(vm._xapiId, @getXapi(host), host._xapiId, {
|
||||
migrationNetworkId: migrationNetwork?._xapiId
|
||||
@ -450,7 +450,7 @@ set = (params) ->
|
||||
return @allocateLimitsInResourceSet(limits, resourceSet)
|
||||
|
||||
if (limits.cpuWeight && this.user.permission != 'admin')
|
||||
throw new Unauthorized()
|
||||
throw unauthorized()
|
||||
)
|
||||
|
||||
set.params = {
|
||||
@ -596,7 +596,7 @@ convertToTemplate = $coroutine ({vm}) ->
|
||||
unless yield @hasPermissions(@session.get('user_id'), [
|
||||
[ vm.$pool, 'administrate' ]
|
||||
])
|
||||
throw new Unauthorized()
|
||||
throw unauthorized()
|
||||
|
||||
yield @getXapi(vm).call 'VM.set_is_a_template', vm._xapiRef, true
|
||||
|
||||
@ -791,10 +791,10 @@ exports.rollingBackup = rollingBackup
|
||||
rollingDrCopy = ({vm, pool, sr, tag, depth}) ->
|
||||
unless sr
|
||||
unless pool
|
||||
throw new InvalidParameters('either pool or sr param should be specified')
|
||||
throw invalidParameters('either pool or sr param should be specified')
|
||||
|
||||
if vm.$pool is pool.id
|
||||
throw new GenericError('Disaster Recovery attempts to copy on the same pool')
|
||||
throw forbiddenOperation('Disaster Recovery attempts to copy on the same pool')
|
||||
|
||||
sr = @getObject(pool.default_SR, 'SR')
|
||||
|
||||
@ -852,7 +852,7 @@ stop = $coroutine ({vm, force}) ->
|
||||
yield xapi.call 'VM.clean_shutdown', vm._xapiRef
|
||||
catch error
|
||||
if error.code is 'VM_MISSING_PV_DRIVERS' or error.code is 'VM_LACKS_FEATURE_SHUTDOWN'
|
||||
throw new InvalidParameters('clean shutdown requires PV drivers')
|
||||
throw invalidParameters('clean shutdown requires PV drivers')
|
||||
else
|
||||
throw error
|
||||
|
||||
@ -983,7 +983,7 @@ handleVmImport = $coroutine (req, res, { data, srId, type, xapi }) ->
|
||||
res.end(format.response(0, vm.$id))
|
||||
catch e
|
||||
res.writeHead(500)
|
||||
res.end(format.error(0, new GenericError(e.message)))
|
||||
res.end(format.error(0, new Error(e.message)))
|
||||
|
||||
return
|
||||
|
||||
@ -991,12 +991,12 @@ handleVmImport = $coroutine (req, res, { data, srId, type, xapi }) ->
|
||||
import_ = $coroutine ({ data, host, sr, type }) ->
|
||||
if not sr
|
||||
if not host
|
||||
throw new InvalidParameters('you must provide either host or SR')
|
||||
throw invalidParameters('you must provide either host or SR')
|
||||
|
||||
xapi = @getXapi(host)
|
||||
sr = xapi.pool.$default_SR
|
||||
if not sr
|
||||
throw new InvalidParameters('there is not default SR in this pool')
|
||||
throw invalidParameters('there is not default SR in this pool')
|
||||
|
||||
# FIXME: must have administrate permission on default SR.
|
||||
else
|
||||
@ -1203,7 +1203,7 @@ setBootOrder = $coroutine ({vm, order}) ->
|
||||
yield xapi.call 'VM.set_HVM_boot_params', vm._xapiRef, order
|
||||
return true
|
||||
|
||||
throw new InvalidParameters('You can only set the boot order on a HVM guest')
|
||||
throw invalidParameters('You can only set the boot order on a HVM guest')
|
||||
|
||||
setBootOrder.params = {
|
||||
vm: { type: 'string' },
|
||||
|
@ -18,7 +18,7 @@ import { createServer as createProxyServer } from 'http-proxy'
|
||||
import { join as joinPath } from 'path'
|
||||
|
||||
import JsonRpcPeer from 'json-rpc-peer'
|
||||
import { InvalidCredential } from './api-errors'
|
||||
import { invalidCredentials } from 'xo-common/api-errors'
|
||||
import {
|
||||
readFile,
|
||||
readdir
|
||||
@ -475,7 +475,7 @@ const setUpConsoleProxy = (webServer, xo) => {
|
||||
|
||||
const user = await xo.authenticateUser({ token })
|
||||
if (!await xo.hasPermissions(user.id, [ [ id, 'operate' ] ])) {
|
||||
throw new InvalidCredential()
|
||||
throw invalidCredentials()
|
||||
}
|
||||
|
||||
const { remoteAddress } = socket
|
||||
|
@ -40,9 +40,7 @@ import {
|
||||
promisifyAll,
|
||||
pSettle
|
||||
} from '../utils'
|
||||
import {
|
||||
ForbiddenOperation
|
||||
} from '../api-errors'
|
||||
import { forbiddenOperation } from 'xo-common/api-errors'
|
||||
|
||||
import mixins from './mixins'
|
||||
import OTHER_CONFIG_TEMPLATE from './other-config-template'
|
||||
@ -1346,7 +1344,7 @@ export default class Xapi extends XapiBase {
|
||||
await this._startVm(this.getObject(vmId))
|
||||
} catch (e) {
|
||||
if (e.code === 'OPERATION_BLOCKED') {
|
||||
throw new ForbiddenOperation('Start', e.params[1])
|
||||
throw forbiddenOperation('Start', e.params[1])
|
||||
}
|
||||
|
||||
throw e
|
||||
|
@ -6,7 +6,6 @@ import unzip from 'julien-f-unzip'
|
||||
import httpProxy from '../../http-proxy'
|
||||
import httpRequest from '../../http-request'
|
||||
import { debounce } from '../../decorators'
|
||||
import { GenericError } from '../../api-errors'
|
||||
import {
|
||||
createRawObject,
|
||||
ensureArray,
|
||||
@ -30,7 +29,7 @@ export default {
|
||||
)
|
||||
|
||||
if (statusCode !== 200) {
|
||||
throw new GenericError('cannot fetch patches list from Citrix')
|
||||
throw new Error('cannot fetch patches list from Citrix')
|
||||
}
|
||||
|
||||
const data = parseXml(await readAll()).patchdata
|
||||
|
@ -7,10 +7,8 @@ import schemaInspector from 'schema-inspector'
|
||||
|
||||
import * as methods from '../api'
|
||||
import {
|
||||
MethodNotFound,
|
||||
InvalidParameters,
|
||||
Unauthorized
|
||||
} from '../api-errors'
|
||||
MethodNotFound
|
||||
} from 'json-rpc-peer'
|
||||
import {
|
||||
createRawObject,
|
||||
forEach,
|
||||
@ -19,6 +17,8 @@ import {
|
||||
serializeError
|
||||
} from '../utils'
|
||||
|
||||
import * as errors from 'xo-common/api-errors'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const PERMISSIONS = {
|
||||
@ -28,6 +28,12 @@ const PERMISSIONS = {
|
||||
admin: 3
|
||||
}
|
||||
|
||||
const XAPI_ERROR_TO_XO_ERROR = {
|
||||
NO_HOSTS_AVAILABLE: errors.noHostsAvailable,
|
||||
SESSION_AUTHENTICATION_FAILED: errors.authenticationFailed,
|
||||
EHOSTUNREACH: errors.hostUnreached
|
||||
}
|
||||
|
||||
const hasPermission = (user, permission) => (
|
||||
PERMISSIONS[user.permission] >= PERMISSIONS[permission]
|
||||
)
|
||||
@ -44,7 +50,7 @@ function checkParams (method, params) {
|
||||
}, params)
|
||||
|
||||
if (!result.valid) {
|
||||
throw new InvalidParameters(result.error)
|
||||
throw errors.invalidParameters(result.error)
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +66,7 @@ function checkPermission (method) {
|
||||
|
||||
const {user} = this
|
||||
if (!user) {
|
||||
throw new Unauthorized()
|
||||
throw errors.unauthorized()
|
||||
}
|
||||
|
||||
// The only requirement is login.
|
||||
@ -69,7 +75,7 @@ function checkPermission (method) {
|
||||
}
|
||||
|
||||
if (!hasPermission(user, permission)) {
|
||||
throw new Unauthorized()
|
||||
throw errors.unauthorized()
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,7 +87,7 @@ function resolveParams (method, params) {
|
||||
|
||||
const {user} = this
|
||||
if (!user) {
|
||||
throw new Unauthorized()
|
||||
throw errors.unauthorized()
|
||||
}
|
||||
|
||||
const userId = user.id
|
||||
@ -117,7 +123,7 @@ function resolveParams (method, params) {
|
||||
return params
|
||||
}
|
||||
|
||||
throw new Unauthorized()
|
||||
throw errors.unauthorized()
|
||||
})
|
||||
}
|
||||
|
||||
@ -282,6 +288,11 @@ export default class Api {
|
||||
)
|
||||
}
|
||||
|
||||
const xoError = XAPI_ERROR_TO_XO_ERROR[error.code]
|
||||
if (xoError) {
|
||||
throw xoError.error(error.params)
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
import Token, { Tokens } from '../models/token'
|
||||
import {
|
||||
NoSuchObject
|
||||
} from '../api-errors'
|
||||
import { noSuchObject } from 'xo-common/api-errors'
|
||||
import {
|
||||
createRawObject,
|
||||
forEach,
|
||||
@ -12,13 +10,8 @@ import {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
class NoSuchAuthenticationToken extends NoSuchObject {
|
||||
constructor (id) {
|
||||
super(id, 'authentication token')
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
const noSuchAuthenticationToken = id =>
|
||||
noSuchObject(id, 'authenticationToken')
|
||||
|
||||
export default class {
|
||||
constructor (xo) {
|
||||
@ -172,14 +165,14 @@ export default class {
|
||||
|
||||
async deleteAuthenticationToken (id) {
|
||||
if (!await this._tokens.remove(id)) {
|
||||
throw new NoSuchAuthenticationToken(id)
|
||||
throw noSuchAuthenticationToken(id)
|
||||
}
|
||||
}
|
||||
|
||||
async getAuthenticationToken (id) {
|
||||
let token = await this._tokens.first(id)
|
||||
if (!token) {
|
||||
throw new NoSuchAuthenticationToken(id)
|
||||
throw noSuchAuthenticationToken(id)
|
||||
}
|
||||
|
||||
token = token.properties
|
||||
@ -189,7 +182,7 @@ export default class {
|
||||
)) {
|
||||
this._tokens.remove(id)::pCatch(noop)
|
||||
|
||||
throw new NoSuchAuthenticationToken(id)
|
||||
throw noSuchAuthenticationToken(id)
|
||||
}
|
||||
|
||||
return token
|
||||
|
@ -8,9 +8,9 @@ import keys from 'lodash/keys'
|
||||
import mapValues from 'lodash/mapValues'
|
||||
import pick from 'lodash/pick'
|
||||
import remove from 'lodash/remove'
|
||||
import { noSuchObject } from 'xo-common/api-errors'
|
||||
import { fromCallback } from 'promise-toolbox'
|
||||
|
||||
import { NoSuchObject } from '../api-errors'
|
||||
import {
|
||||
forEach,
|
||||
generateUnsecureToken,
|
||||
@ -23,12 +23,6 @@ import {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
class NoSuchIpPool extends NoSuchObject {
|
||||
constructor (id) {
|
||||
super(id, 'ip pool')
|
||||
}
|
||||
}
|
||||
|
||||
const normalize = ({
|
||||
addresses,
|
||||
id = throwFn('id is a required field'),
|
||||
@ -90,7 +84,7 @@ export default class IpPools {
|
||||
return store.del(id)
|
||||
}
|
||||
|
||||
throw new NoSuchIpPool(id)
|
||||
throw noSuchObject(id, 'ipPool')
|
||||
}
|
||||
|
||||
async getAllIpPools (userId = undefined) {
|
||||
@ -112,7 +106,7 @@ export default class IpPools {
|
||||
|
||||
getIpPool (id) {
|
||||
return this._store.get(id).then(normalize, error => {
|
||||
throw error.notFound ? new NoSuchIpPool(id) : error
|
||||
throw error.notFound ? noSuchObject(id, 'ipPool') : error
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -3,18 +3,7 @@ import assign from 'lodash/assign'
|
||||
import JobExecutor from '../job-executor'
|
||||
import { Jobs } from '../models/job'
|
||||
import { mapToArray } from '../utils'
|
||||
import {
|
||||
GenericError,
|
||||
NoSuchObject
|
||||
} from '../api-errors'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
class NoSuchJob extends NoSuchObject {
|
||||
constructor (id) {
|
||||
super(id, 'job')
|
||||
}
|
||||
}
|
||||
import { noSuchObject } from 'xo-common/api-errors'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@ -44,7 +33,7 @@ export default class {
|
||||
async getJob (id) {
|
||||
const job = await this._jobs.first(id)
|
||||
if (!job) {
|
||||
throw new NoSuchJob(id)
|
||||
throw noSuchObject(id, 'job')
|
||||
}
|
||||
|
||||
return job.properties
|
||||
@ -67,24 +56,10 @@ export default class {
|
||||
}
|
||||
|
||||
async runJobSequence (idSequence) {
|
||||
const notFound = []
|
||||
for (const id of idSequence) {
|
||||
let job
|
||||
try {
|
||||
job = await this.getJob(id)
|
||||
} catch (error) {
|
||||
if (error instanceof NoSuchJob) {
|
||||
notFound.push(id)
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
if (job) {
|
||||
await this._executor.exec(job)
|
||||
}
|
||||
}
|
||||
if (notFound.length > 0) {
|
||||
throw new GenericError(`The following jobs were not found: ${notFound.join()}`)
|
||||
const jobs = await Promise.all(mapToArray(idSequence, id => this.getJob(id)))
|
||||
|
||||
for (const job of jobs) {
|
||||
await this._executor.exec(job)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,9 @@ import createJsonSchemaValidator from 'is-my-json-valid'
|
||||
|
||||
import { PluginsMetadata } from '../models/plugin-metadata'
|
||||
import {
|
||||
InvalidParameters,
|
||||
NoSuchObject
|
||||
} from '../api-errors'
|
||||
invalidParameters,
|
||||
noSuchObject
|
||||
} from 'xo-common/api-errors'
|
||||
import {
|
||||
createRawObject,
|
||||
isFunction,
|
||||
@ -13,14 +13,6 @@ import {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
class NoSuchPlugin extends NoSuchObject {
|
||||
constructor (id) {
|
||||
super(id, 'plugin')
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export default class {
|
||||
constructor (xo) {
|
||||
this._plugins = createRawObject()
|
||||
@ -43,7 +35,7 @@ export default class {
|
||||
_getRawPlugin (id) {
|
||||
const plugin = this._plugins[id]
|
||||
if (!plugin) {
|
||||
throw new NoSuchPlugin(id)
|
||||
throw noSuchObject(id, 'plugin')
|
||||
}
|
||||
return plugin
|
||||
}
|
||||
@ -148,12 +140,12 @@ export default class {
|
||||
const { configurationSchema } = plugin
|
||||
|
||||
if (!configurationSchema) {
|
||||
throw new InvalidParameters('plugin not configurable')
|
||||
throw invalidParameters('plugin not configurable')
|
||||
}
|
||||
|
||||
// See: https://github.com/mafintosh/is-my-json-valid/issues/116
|
||||
if (configuration == null) {
|
||||
throw new InvalidParameters([{
|
||||
throw invalidParameters([{
|
||||
field: 'data',
|
||||
message: 'is the wrong type'
|
||||
}])
|
||||
@ -161,7 +153,7 @@ export default class {
|
||||
|
||||
const validate = createJsonSchemaValidator(configurationSchema)
|
||||
if (!validate(configuration)) {
|
||||
throw new InvalidParameters(validate.errors)
|
||||
throw invalidParameters(validate.errors)
|
||||
}
|
||||
|
||||
// Sets the plugin configuration.
|
||||
@ -200,11 +192,11 @@ export default class {
|
||||
async loadPlugin (id) {
|
||||
const plugin = this._getRawPlugin(id)
|
||||
if (plugin.loaded) {
|
||||
throw new InvalidParameters('plugin already loaded')
|
||||
throw invalidParameters('plugin already loaded')
|
||||
}
|
||||
|
||||
if (!plugin.configured) {
|
||||
throw new InvalidParameters('plugin not configured')
|
||||
throw invalidParameters('plugin not configured')
|
||||
}
|
||||
|
||||
await plugin.instance.load()
|
||||
@ -214,11 +206,11 @@ export default class {
|
||||
async unloadPlugin (id) {
|
||||
const plugin = this._getRawPlugin(id)
|
||||
if (!plugin.loaded) {
|
||||
throw new InvalidParameters('plugin already unloaded')
|
||||
throw invalidParameters('plugin already unloaded')
|
||||
}
|
||||
|
||||
if (plugin.unloadable === false) {
|
||||
throw new InvalidParameters('plugin cannot be unloaded')
|
||||
throw invalidParameters('plugin cannot be unloaded')
|
||||
}
|
||||
|
||||
await plugin.instance.unload()
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { noSuchObject } from 'xo-common/api-errors'
|
||||
|
||||
import RemoteHandlerLocal from '../remote-handlers/local'
|
||||
import RemoteHandlerNfs from '../remote-handlers/nfs'
|
||||
import RemoteHandlerSmb from '../remote-handlers/smb'
|
||||
@ -5,23 +7,12 @@ import {
|
||||
forEach,
|
||||
mapToArray
|
||||
} from '../utils'
|
||||
import {
|
||||
NoSuchObject
|
||||
} from '../api-errors'
|
||||
import {
|
||||
Remotes
|
||||
} from '../models/remote'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
class NoSuchRemote extends NoSuchObject {
|
||||
constructor (id) {
|
||||
super(id, 'remote')
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export default class {
|
||||
constructor (xo) {
|
||||
this._remotes = new Remotes({
|
||||
@ -74,7 +65,7 @@ export default class {
|
||||
async _getRemote (id) {
|
||||
const remote = await this._remotes.first(id)
|
||||
if (!remote) {
|
||||
throw new NoSuchRemote(id)
|
||||
throw noSuchObject(id, 'remote')
|
||||
}
|
||||
|
||||
return remote
|
||||
|
@ -2,11 +2,11 @@ import every from 'lodash/every'
|
||||
import keyBy from 'lodash/keyBy'
|
||||
import remove from 'lodash/remove'
|
||||
import some from 'lodash/some'
|
||||
|
||||
import {
|
||||
NoSuchObject,
|
||||
Unauthorized
|
||||
} from '../api-errors'
|
||||
noSuchObject,
|
||||
unauthorized
|
||||
} from 'xo-common/api-errors'
|
||||
|
||||
import {
|
||||
forEach,
|
||||
generateUnsecureToken,
|
||||
@ -19,12 +19,6 @@ import {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
class NoSuchResourceSet extends NoSuchObject {
|
||||
constructor (id) {
|
||||
super(id, 'resource set')
|
||||
}
|
||||
}
|
||||
|
||||
const VM_RESOURCES = {
|
||||
cpus: true,
|
||||
disk: true,
|
||||
@ -124,7 +118,7 @@ export default class {
|
||||
// The set does not contains ALL objects.
|
||||
!every(objectIds, lightSet(set.objects).has)
|
||||
)) {
|
||||
throw new Unauthorized()
|
||||
throw unauthorized()
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,7 +150,7 @@ export default class {
|
||||
return store.del(id)
|
||||
}
|
||||
|
||||
throw new NoSuchResourceSet(id)
|
||||
throw noSuchObject(id, 'resourceSet')
|
||||
}
|
||||
|
||||
async updateResourceSet (id, {
|
||||
@ -223,7 +217,7 @@ export default class {
|
||||
getResourceSet (id) {
|
||||
return this._store.get(id).then(normalize, error => {
|
||||
if (error.notFound) {
|
||||
throw new NoSuchResourceSet(id)
|
||||
throw noSuchObject(id, 'resourceSet')
|
||||
}
|
||||
|
||||
throw error
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { BaseError } from 'make-error'
|
||||
import { NoSuchObject } from '../api-errors.js'
|
||||
import { Schedules } from '../models/schedule'
|
||||
import { noSuchObject } from 'xo-common/api-errors.js'
|
||||
|
||||
import { Schedules } from '../models/schedule'
|
||||
import {
|
||||
forEach,
|
||||
mapToArray,
|
||||
@ -20,12 +20,6 @@ export class ScheduleOverride extends SchedulerError {
|
||||
}
|
||||
}
|
||||
|
||||
export class NoSuchSchedule extends NoSuchObject {
|
||||
constructor (scheduleOrId) {
|
||||
super(scheduleOrId, 'schedule')
|
||||
}
|
||||
}
|
||||
|
||||
export class ScheduleNotEnabled extends SchedulerError {
|
||||
constructor (scheduleOrId) {
|
||||
super('Schedule ' + _resolveId(scheduleOrId)) + ' is not enabled'
|
||||
@ -96,7 +90,7 @@ export default class {
|
||||
|
||||
_disable (scheduleOrId) {
|
||||
if (!this._exists(scheduleOrId)) {
|
||||
throw new NoSuchSchedule(scheduleOrId)
|
||||
throw noSuchObject(scheduleOrId, 'schedule')
|
||||
}
|
||||
if (!this._isEnabled(scheduleOrId)) {
|
||||
throw new ScheduleNotEnabled(scheduleOrId)
|
||||
@ -135,7 +129,7 @@ export default class {
|
||||
const schedule = await this._redisSchedules.first(id)
|
||||
|
||||
if (!schedule) {
|
||||
throw new NoSuchSchedule(id)
|
||||
throw noSuchObject(id, 'schedule')
|
||||
}
|
||||
|
||||
return schedule
|
||||
@ -176,7 +170,7 @@ export default class {
|
||||
const { properties } = schedule
|
||||
|
||||
if (!this._exists(properties)) {
|
||||
throw new NoSuchSchedule(properties)
|
||||
throw noSuchObject(properties, 'schedule')
|
||||
}
|
||||
|
||||
if (this._isEnabled(properties)) {
|
||||
|
@ -5,11 +5,11 @@ import {
|
||||
needsRehash,
|
||||
verify
|
||||
} from 'hashy'
|
||||
|
||||
import {
|
||||
InvalidCredential,
|
||||
NoSuchObject
|
||||
} from '../api-errors'
|
||||
invalidCredentials,
|
||||
noSuchObject
|
||||
} from 'xo-common/api-errors'
|
||||
|
||||
import {
|
||||
Groups
|
||||
} from '../models/group'
|
||||
@ -27,18 +27,6 @@ import {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
class NoSuchGroup extends NoSuchObject {
|
||||
constructor (id) {
|
||||
super(id, 'group')
|
||||
}
|
||||
}
|
||||
|
||||
class NoSuchUser extends NoSuchObject {
|
||||
constructor (id) {
|
||||
super(id, 'user')
|
||||
}
|
||||
}
|
||||
|
||||
const addToArraySet = (set, value) => set && !includes(set, value)
|
||||
? set.concat(value)
|
||||
: [ value ]
|
||||
@ -181,7 +169,7 @@ export default class {
|
||||
async _getUser (id) {
|
||||
const user = await this._users.first(id)
|
||||
if (!user) {
|
||||
throw new NoSuchUser(id)
|
||||
throw noSuchObject(id, 'user')
|
||||
}
|
||||
|
||||
return user
|
||||
@ -214,7 +202,7 @@ export default class {
|
||||
return null
|
||||
}
|
||||
|
||||
throw new NoSuchUser(username)
|
||||
throw noSuchObject(username, 'user')
|
||||
}
|
||||
|
||||
// Get or create a user associated with an auth provider.
|
||||
@ -240,7 +228,7 @@ export default class {
|
||||
|
||||
async changeUserPassword (userId, oldPassword, newPassword) {
|
||||
if (!(await this.checkUserPassword(userId, oldPassword, false))) {
|
||||
throw new InvalidCredential()
|
||||
throw invalidCredentials()
|
||||
}
|
||||
|
||||
await this.updateUser(userId, { password: newPassword })
|
||||
@ -303,7 +291,7 @@ export default class {
|
||||
async getGroup (id) {
|
||||
const group = await this._groups.first(id)
|
||||
if (!group) {
|
||||
throw new NoSuchGroup(id)
|
||||
throw noSuchObject(id, 'group')
|
||||
}
|
||||
|
||||
return group.properties
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { noSuchObject } from 'xo-common/api-errors'
|
||||
|
||||
import Xapi from '../xapi'
|
||||
import xapiObjectToXo from '../xapi-object-to-xo'
|
||||
import XapiStats from '../xapi-stats'
|
||||
import {
|
||||
GenericError,
|
||||
NoSuchObject
|
||||
} from '../api-errors'
|
||||
import {
|
||||
camelToSnakeCase,
|
||||
createRawObject,
|
||||
@ -21,14 +19,6 @@ import {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
class NoSuchXenServer extends NoSuchObject {
|
||||
constructor (id) {
|
||||
super(id, 'xen server')
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export default class {
|
||||
constructor (xo) {
|
||||
this._objectConflicts = createRawObject() // TODO: clean when a server is disconnected.
|
||||
@ -84,7 +74,7 @@ export default class {
|
||||
this.disconnectXenServer(id)::pCatch(noop)
|
||||
|
||||
if (!await this._servers.remove(id)) {
|
||||
throw new NoSuchXenServer(id)
|
||||
throw noSuchObject(id, 'xenServer')
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,7 +105,7 @@ export default class {
|
||||
async _getXenServer (id) {
|
||||
const server = await this._servers.first(id)
|
||||
if (!server) {
|
||||
throw new NoSuchXenServer(id)
|
||||
throw noSuchObject(id, 'xenServer')
|
||||
}
|
||||
|
||||
return server
|
||||
@ -288,23 +278,13 @@ export default class {
|
||||
|
||||
xapi.xo.install()
|
||||
|
||||
try {
|
||||
await xapi.connect()
|
||||
} catch (error) {
|
||||
if (error.code === 'SESSION_AUTHENTICATION_FAILED') {
|
||||
throw new GenericError('authentication failed')
|
||||
}
|
||||
if (error.code === 'EHOSTUNREACH') {
|
||||
throw new GenericError('host unreachable')
|
||||
}
|
||||
throw error
|
||||
}
|
||||
await xapi.connect()
|
||||
}
|
||||
|
||||
async disconnectXenServer (id) {
|
||||
const xapi = this._xapis[id]
|
||||
if (!xapi) {
|
||||
throw new NoSuchXenServer(id)
|
||||
throw noSuchObject(id, 'xenServer')
|
||||
}
|
||||
|
||||
delete this._xapis[id]
|
||||
|
@ -3,6 +3,7 @@ import XoCollection from 'xo-collection'
|
||||
import XoUniqueIndex from 'xo-collection/unique-index'
|
||||
import {createClient as createRedisClient} from 'redis'
|
||||
import {EventEmitter} from 'events'
|
||||
import { noSuchObject } from 'xo-common/api-errors'
|
||||
|
||||
import mixins from './xo-mixins'
|
||||
import Connection from './connection'
|
||||
@ -20,9 +21,6 @@ import {
|
||||
mapToArray,
|
||||
noop
|
||||
} from './utils'
|
||||
import {
|
||||
NoSuchObject
|
||||
} from './api-errors'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@ -140,14 +138,14 @@ export default class Xo extends EventEmitter {
|
||||
|
||||
const obj = all[key] || byRef[key]
|
||||
if (!obj) {
|
||||
throw new NoSuchObject(key, type)
|
||||
throw noSuchObject(key, type)
|
||||
}
|
||||
|
||||
if (type != null && (
|
||||
isString(type) && type !== obj.type ||
|
||||
!includes(type, obj.type) // Array
|
||||
)) {
|
||||
throw new NoSuchObject(key, type)
|
||||
throw noSuchObject(key, type)
|
||||
}
|
||||
|
||||
return obj
|
||||
|
Loading…
Reference in New Issue
Block a user