Compare commits
54 Commits
xo-server-
...
complex-ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4ab028ad6 | ||
|
|
9fc6013934 | ||
|
|
8a02d557f4 | ||
|
|
bdddea2e29 | ||
|
|
be4ca0eede | ||
|
|
316d71b34d | ||
|
|
b3bc9a2edd | ||
|
|
90369acb63 | ||
|
|
68cef1aa5d | ||
|
|
2f9ecb8fa8 | ||
|
|
6281374cf3 | ||
|
|
6f6e760a25 | ||
|
|
38fb549709 | ||
|
|
d4e46a0696 | ||
|
|
2d0e3b93cc | ||
|
|
668d572549 | ||
|
|
e023809c8c | ||
|
|
2174a9cec7 | ||
|
|
79ea50c829 | ||
|
|
8532d563de | ||
|
|
2e945e8349 | ||
|
|
22e3037e85 | ||
|
|
a505ded8a1 | ||
|
|
bea7b90eb2 | ||
|
|
e1eb948c54 | ||
|
|
1b42523e2d | ||
|
|
ed7a40bc73 | ||
|
|
5b7a9fe969 | ||
|
|
97d54cab11 | ||
|
|
980a1cc205 | ||
|
|
9517686e22 | ||
|
|
bc02d51882 | ||
|
|
aada9e4a33 | ||
|
|
abf526f91a | ||
|
|
c06020745e | ||
|
|
431b85c98f | ||
|
|
299fdc19d6 | ||
|
|
bf3f9b4ac2 | ||
|
|
4c91667d2c | ||
|
|
ed45888d7f | ||
|
|
dbadc487ae | ||
|
|
a5b80655da | ||
|
|
99d4789049 | ||
|
|
2de4163553 | ||
|
|
0fc0be19b2 | ||
|
|
3c271ffffd | ||
|
|
1aa793886b | ||
|
|
46cd2f10a5 | ||
|
|
337abe2a2b | ||
|
|
0441103f0c | ||
|
|
b5829e2484 | ||
|
|
e24cba9684 | ||
|
|
084e96be0e | ||
|
|
5dfefe8d35 |
15
.eslintrc.js
Normal file
15
.eslintrc.js
Normal file
@@ -0,0 +1,15 @@
|
||||
module.exports = {
|
||||
'extends': [
|
||||
'standard',
|
||||
],
|
||||
'parser': 'babel-eslint',
|
||||
'rules': {
|
||||
'comma-dangle': ['error', 'always-multiline'],
|
||||
'no-var': 'error',
|
||||
'node/no-extraneous-import': 'error',
|
||||
'node/no-extraneous-require': 'error',
|
||||
'node/no-missing-import': 'error',
|
||||
'node/no-missing-require': 'error',
|
||||
'prefer-const': 'error',
|
||||
},
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
[lints]
|
||||
|
||||
[options]
|
||||
experimental.const_params=true
|
||||
include_warnings=true
|
||||
module.use_strict=true
|
||||
unsafe.enable_getters_and_setters=true
|
||||
|
||||
[strict]
|
||||
@@ -1,6 +1,7 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- stable
|
||||
- 8
|
||||
- 6
|
||||
|
||||
# Use containers.
|
||||
|
||||
44
package.json
44
package.json
@@ -1,12 +1,21 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^8.0.1",
|
||||
"babel-7-jest": "^21.3.2",
|
||||
"babel-eslint": "^8.1.2",
|
||||
"eslint": "^4.14.0",
|
||||
"eslint-config-standard": "^11.0.0-beta.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"eslint-plugin-node": "^5.2.1",
|
||||
"eslint-plugin-promise": "^3.6.0",
|
||||
"eslint-plugin-standard": "^3.0.1",
|
||||
"exec-promise": "^0.7.0",
|
||||
"flow-bin": "^0.63.1",
|
||||
"husky": "^0.14.3",
|
||||
"jest": "^21.2.1",
|
||||
"lint-staged": "^5.0.0",
|
||||
"jest": "^22.0.4",
|
||||
"lint-staged": "^6.0.0",
|
||||
"lodash": "^4.17.4",
|
||||
"promise-toolbox": "^0.9.5",
|
||||
"standard": "^10.0.3"
|
||||
"sorted-object": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"yarn": "^1.2.1"
|
||||
@@ -18,31 +27,34 @@
|
||||
"/dist/",
|
||||
"/xo-vmdk-to-vhd/"
|
||||
],
|
||||
"testRegex": "\\.spec\\.js$"
|
||||
"testRegex": "\\.spec\\.js$",
|
||||
"transform": {
|
||||
"complex-matcher/.+\\.jsx?$": "babel-7-jest",
|
||||
"value-matcher/.+\\.jsx?$": "babel-7-jest",
|
||||
"xo-cli/.+\\.jsx?$": "babel-7-jest",
|
||||
"\\.jsx?$": "babel-jest"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"lint-staged-stash",
|
||||
"standard --fix",
|
||||
"jest --findRelatedTests",
|
||||
"eslint --fix",
|
||||
"jest --findRelatedTests --passWithNoTests",
|
||||
"lint-staged-unstash"
|
||||
]
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "scripts/run-script --parallel build",
|
||||
"clean": "scripts/run-script --parallel clean",
|
||||
"dev-test": "jest --bail --watch",
|
||||
"lint-staged-stash": "touch .lint-staged && git stash save --include-untracked --keep-index && true",
|
||||
"lint-staged-unstash": "git stash pop && rm -f .lint-staged && true",
|
||||
"posttest": "scripts/run-script test",
|
||||
"precommit": "lint-staged",
|
||||
"prepublish": "scripts/run-script prepublish",
|
||||
"pretest": "standard && jest",
|
||||
"test": "scripts/run-script test"
|
||||
},
|
||||
"standard": {
|
||||
"ignore": [
|
||||
"packages/*/dist"
|
||||
],
|
||||
"parser": "babel-eslint"
|
||||
"prepare": "scripts/run-script prepare",
|
||||
"pretest": "eslint --ignore-path .gitignore .",
|
||||
"test": "jest && flow status"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
|
||||
28
packages/complex-matcher/.babelrc.js
Normal file
28
packages/complex-matcher/.babelrc.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const dependencies = require('./package').dependencies || {}
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV || 'development'
|
||||
const __PROD__ = NODE_ENV === 'production'
|
||||
const __TEST__ = NODE_ENV === 'test'
|
||||
|
||||
module.exports = {
|
||||
comments: !__PROD__,
|
||||
ignore: __TEST__ ? undefined : [/\.spec\.js$/],
|
||||
plugins: ['lodash'],
|
||||
presets: [
|
||||
[
|
||||
'@babel/env',
|
||||
{
|
||||
debug: !__TEST__,
|
||||
loose: true,
|
||||
shippedProposals: true,
|
||||
targets: __PROD__
|
||||
? {
|
||||
browsers: '>2%',
|
||||
node: '4',
|
||||
}
|
||||
: { node: 'current' },
|
||||
useBuiltIns: '@babel/polyfill' in dependencies && 'usage',
|
||||
},
|
||||
],
|
||||
],
|
||||
}
|
||||
24
packages/complex-matcher/.npmignore
Normal file
24
packages/complex-matcher/.npmignore
Normal file
@@ -0,0 +1,24 @@
|
||||
/benchmark/
|
||||
/benchmarks/
|
||||
*.bench.js
|
||||
*.bench.js.map
|
||||
|
||||
/examples/
|
||||
example.js
|
||||
example.js.map
|
||||
*.example.js
|
||||
*.example.js.map
|
||||
|
||||
/fixture/
|
||||
/fixtures/
|
||||
*.fixture.js
|
||||
*.fixture.js.map
|
||||
*.fixtures.js
|
||||
*.fixtures.js.map
|
||||
|
||||
/test/
|
||||
/tests/
|
||||
*.spec.js
|
||||
*.spec.js.map
|
||||
|
||||
__snapshots__/
|
||||
66
packages/complex-matcher/README.md
Normal file
66
packages/complex-matcher/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# complex-matcher [](https://travis-ci.org/vatesfr/xen-orchestra)
|
||||
|
||||
> ${pkg.description}
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/complex-matcher):
|
||||
|
||||
```
|
||||
> npm install --save complex-matcher
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import * as CM from 'complex-matcher'
|
||||
|
||||
const characters = [
|
||||
{ name: 'Catwoman', costumeColor: 'black' },
|
||||
{ name: 'Superman', costumeColor: 'blue', hasCape: true },
|
||||
{ name: 'Wonder Woman', costumeColor: 'blue' },
|
||||
]
|
||||
|
||||
const predicate = CM.parse('costumeColor:blue hasCape?').createPredicate()
|
||||
|
||||
characters.filter(predicate)
|
||||
// [
|
||||
// { name: 'Superman', costumeColor: 'blue', hasCape: true },
|
||||
// ]
|
||||
|
||||
new CM.String('foo').createPredicate()
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
```
|
||||
# Install dependencies
|
||||
> yarn
|
||||
|
||||
# Run the tests
|
||||
> yarn test
|
||||
|
||||
# Continuously compile
|
||||
> yarn dev
|
||||
|
||||
# Continuously run the tests
|
||||
> yarn dev-test
|
||||
|
||||
# Build for production (automatically called by npm install)
|
||||
> yarn build
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are *very* welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xo-web/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
ISC © [Vates SAS](https://vates.fr)
|
||||
45
packages/complex-matcher/package.json
Normal file
45
packages/complex-matcher/package.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "complex-matcher",
|
||||
"version": "0.2.0",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/complex-matcher",
|
||||
"bugs": "https://github.com/vatesfr/xo-web/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Julien Fontanet",
|
||||
"email": "julien.fontanet@isonoe.net"
|
||||
},
|
||||
"preferGlobal": false,
|
||||
"main": "dist/",
|
||||
"bin": {},
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.37",
|
||||
"@babel/core": "7.0.0-beta.37",
|
||||
"@babel/preset-env": "7.0.0-beta.37",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"cross-env": "^5.1.1",
|
||||
"rimraf": "^2.6.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"clean": "rimraf dist/",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run clean",
|
||||
"prepublishOnly": "yarn run build"
|
||||
}
|
||||
}
|
||||
12
packages/complex-matcher/src/index.bench.js
Normal file
12
packages/complex-matcher/src/index.bench.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { parse } from './'
|
||||
import { ast, pattern } from './index.fixtures'
|
||||
|
||||
export default ({ benchmark }) => {
|
||||
benchmark('parse', () => {
|
||||
parse(pattern)
|
||||
})
|
||||
|
||||
benchmark('toString', () => {
|
||||
ast.toString()
|
||||
})
|
||||
}
|
||||
15
packages/complex-matcher/src/index.fixtures.js
Normal file
15
packages/complex-matcher/src/index.fixtures.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import * as CM from './'
|
||||
|
||||
export const pattern =
|
||||
'foo !"\\\\ \\"" name:|(wonderwoman batman) hasCape? age:32'
|
||||
|
||||
export const ast = new CM.And([
|
||||
new CM.String('foo'),
|
||||
new CM.Not(new CM.String('\\ "')),
|
||||
new CM.Property(
|
||||
'name',
|
||||
new CM.Or([new CM.String('wonderwoman'), new CM.String('batman')])
|
||||
),
|
||||
new CM.TruthyProperty('hasCape'),
|
||||
new CM.Property('age', new CM.Number(32)),
|
||||
])
|
||||
470
packages/complex-matcher/src/index.js
Normal file
470
packages/complex-matcher/src/index.js
Normal file
@@ -0,0 +1,470 @@
|
||||
import { isPlainObject, some } from 'lodash'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
// Invoke a function and returns it result.
|
||||
// All parameters are forwarded.
|
||||
//
|
||||
// Why using `invoke()`?
|
||||
// - avoid tedious IIFE syntax
|
||||
// - avoid declaring variables in the common scope
|
||||
// - monkey-patching
|
||||
//
|
||||
// ```js
|
||||
// const sum = invoke(1, 2, (a, b) => a + b)
|
||||
//
|
||||
// eventEmitter.emit = invoke(eventEmitter.emit, emit => function (event) {
|
||||
// if (event === 'foo') {
|
||||
// throw new Error('event foo is disabled')
|
||||
// }
|
||||
//
|
||||
// return emit.apply(this, arguments)
|
||||
// })
|
||||
// ```
|
||||
function invoke (fn) {
|
||||
const n = arguments.length - 1
|
||||
if (!n) {
|
||||
return fn()
|
||||
}
|
||||
|
||||
fn = arguments[n]
|
||||
const args = new Array(n)
|
||||
for (let i = 0; i < n; ++i) {
|
||||
args[i] = arguments[i]
|
||||
}
|
||||
|
||||
return fn.apply(undefined, args)
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const RAW_STRING_CHARS = invoke(() => {
|
||||
const chars = { __proto__: null }
|
||||
const add = (a, b = a) => {
|
||||
let i = a.charCodeAt(0)
|
||||
const j = b.charCodeAt(0)
|
||||
while (i <= j) {
|
||||
chars[String.fromCharCode(i++)] = true
|
||||
}
|
||||
}
|
||||
add('$')
|
||||
add('-')
|
||||
add('.')
|
||||
add('0', '9')
|
||||
add('_')
|
||||
add('A', 'Z')
|
||||
add('a', 'z')
|
||||
return chars
|
||||
})
|
||||
const isRawString = string => {
|
||||
const { length } = string
|
||||
for (let i = 0; i < length; ++i) {
|
||||
if (!(string[i] in RAW_STRING_CHARS)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class Node {
|
||||
createPredicate () {
|
||||
return value => this.match(value)
|
||||
}
|
||||
}
|
||||
|
||||
export class Null extends Node {
|
||||
match () {
|
||||
return true
|
||||
}
|
||||
|
||||
toString () {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
const formatTerms = terms => terms.map(term => term.toString(true)).join(' ')
|
||||
|
||||
export class And extends Node {
|
||||
constructor (children) {
|
||||
super()
|
||||
|
||||
if (children.length === 1) {
|
||||
return children[0]
|
||||
}
|
||||
this.children = children
|
||||
}
|
||||
|
||||
match (value) {
|
||||
return this.children.every(child => child.match(value))
|
||||
}
|
||||
|
||||
toString (isNested) {
|
||||
const terms = formatTerms(this.children)
|
||||
return isNested ? `(${terms})` : terms
|
||||
}
|
||||
}
|
||||
|
||||
export class Or extends Node {
|
||||
constructor (children) {
|
||||
super()
|
||||
|
||||
if (children.length === 1) {
|
||||
return children[0]
|
||||
}
|
||||
this.children = children
|
||||
}
|
||||
|
||||
match (value) {
|
||||
return this.children.some(child => child.match(value))
|
||||
}
|
||||
|
||||
toString () {
|
||||
return `|(${formatTerms(this.children)})`
|
||||
}
|
||||
}
|
||||
|
||||
export class Not extends Node {
|
||||
constructor (child) {
|
||||
super()
|
||||
|
||||
this.child = child
|
||||
}
|
||||
|
||||
match (value) {
|
||||
return !this.child.match(value)
|
||||
}
|
||||
|
||||
toString () {
|
||||
return '!' + this.child.toString(true)
|
||||
}
|
||||
}
|
||||
|
||||
export class NumberNode extends Node {
|
||||
constructor (value) {
|
||||
super()
|
||||
|
||||
this.value = value
|
||||
}
|
||||
|
||||
match (value) {
|
||||
return value === this.value
|
||||
}
|
||||
|
||||
toString () {
|
||||
return String(this.value)
|
||||
}
|
||||
}
|
||||
export { NumberNode as Number }
|
||||
|
||||
export class Property extends Node {
|
||||
constructor (name, child) {
|
||||
super()
|
||||
|
||||
this.name = name
|
||||
this.child = child
|
||||
}
|
||||
|
||||
match (value) {
|
||||
return value != null && this.child.match(value[this.name])
|
||||
}
|
||||
|
||||
toString () {
|
||||
return `${formatString(this.name)}:${this.child.toString(true)}`
|
||||
}
|
||||
}
|
||||
|
||||
const escapeChar = char => '\\' + char
|
||||
const formatString = value =>
|
||||
Number.isNaN(+value)
|
||||
? isRawString(value) ? value : `"${value.replace(/\\|"/g, escapeChar)}"`
|
||||
: `"${value}"`
|
||||
|
||||
export class StringNode extends Node {
|
||||
constructor (value) {
|
||||
super()
|
||||
|
||||
this.lcValue = value.toLowerCase()
|
||||
this.value = value
|
||||
|
||||
// should not be enumerable for the tests
|
||||
Object.defineProperty(this, 'match', {
|
||||
value: this.match.bind(this),
|
||||
})
|
||||
}
|
||||
|
||||
match (value) {
|
||||
if (typeof value === 'string') {
|
||||
return value.toLowerCase().indexOf(this.lcValue) !== -1
|
||||
}
|
||||
|
||||
if (Array.isArray(value) || isPlainObject(value)) {
|
||||
return some(value, this.match)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
toString () {
|
||||
return formatString(this.value)
|
||||
}
|
||||
}
|
||||
export { StringNode as String }
|
||||
|
||||
export class TruthyProperty extends Node {
|
||||
constructor (name) {
|
||||
super()
|
||||
|
||||
this.name = name
|
||||
}
|
||||
|
||||
match (value) {
|
||||
return value != null && !!value[this.name]
|
||||
}
|
||||
|
||||
toString () {
|
||||
return formatString(this.name) + '?'
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// terms = null || term+
|
||||
// *null = /$/
|
||||
// term = ws (and | or | not | property | truthyProperty | numberOrString) ws
|
||||
// ws = ' '*
|
||||
// *and = "(" terms ")"
|
||||
// *or = "|" ws "(" terms ")"
|
||||
// *not = "!" term
|
||||
// *property = string ws ":" term
|
||||
// *truthyProperty = string ws "?"
|
||||
// numberOrString = string
|
||||
// string = quotedString | rawString
|
||||
// quotedString = "\"" ( /[^"\]/ | "\\\\" | "\\\"" )+
|
||||
// rawString = /[a-z0-9-_.]+/i
|
||||
export const parse = invoke(() => {
|
||||
let i
|
||||
let n
|
||||
let input
|
||||
|
||||
// -----
|
||||
|
||||
const backtrace = parser => () => {
|
||||
const pos = i
|
||||
const node = parser()
|
||||
if (node !== undefined) {
|
||||
return node
|
||||
}
|
||||
i = pos
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
const parseTerms = Node => {
|
||||
let term = parseTerm()
|
||||
if (!term) {
|
||||
return new Null()
|
||||
}
|
||||
|
||||
const terms = [term]
|
||||
while ((term = parseTerm())) {
|
||||
terms.push(term)
|
||||
}
|
||||
return new Node(terms)
|
||||
}
|
||||
const parseTerm = () => {
|
||||
parseWs()
|
||||
|
||||
const child =
|
||||
parseAnd() ||
|
||||
parseOr() ||
|
||||
parseNot() ||
|
||||
parseProperty() ||
|
||||
parseTruthyProperty() ||
|
||||
parseNumberOrString()
|
||||
if (child) {
|
||||
parseWs()
|
||||
return child
|
||||
}
|
||||
}
|
||||
const parseWs = () => {
|
||||
while (input[i] === ' ') {
|
||||
++i
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
const parseAnd = backtrace(() => {
|
||||
let and
|
||||
if (input[i++] === '(' && (and = parseTerm(And)) && input[i++] === ')') {
|
||||
return and
|
||||
}
|
||||
})
|
||||
const parseOr = backtrace(() => {
|
||||
let or
|
||||
if (
|
||||
input[i++] === '|' &&
|
||||
parseWs() &&
|
||||
input[i++] === '(' &&
|
||||
(or = parseTerms(Or)) &&
|
||||
input[i++] === ')'
|
||||
) {
|
||||
return or
|
||||
}
|
||||
})
|
||||
const parseNot = backtrace(() => {
|
||||
let child
|
||||
if (input[i++] === '!' && (child = parseTerm())) {
|
||||
return new Not(child)
|
||||
}
|
||||
})
|
||||
const parseProperty = backtrace(() => {
|
||||
let name, child
|
||||
if (
|
||||
(name = parseString()) &&
|
||||
parseWs() &&
|
||||
input[i++] === ':' &&
|
||||
(child = parseTerm())
|
||||
) {
|
||||
return new Property(name, child)
|
||||
}
|
||||
})
|
||||
const parseNumberOrString = () => {
|
||||
let str = parseQuotedString()
|
||||
if (str !== undefined) {
|
||||
return new StringNode(str)
|
||||
}
|
||||
str = parseRawString()
|
||||
if (str !== undefined) {
|
||||
const asNum = +str
|
||||
return Number.isNaN(asNum) ? new StringNode(str) : new NumberNode(asNum)
|
||||
}
|
||||
}
|
||||
const parseString = () => {
|
||||
let value
|
||||
if (
|
||||
(value = parseQuotedString()) !== undefined ||
|
||||
(value = parseRawString()) !== undefined
|
||||
) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
const parseQuotedString = backtrace(() => {
|
||||
if (input[i++] !== '"') {
|
||||
return
|
||||
}
|
||||
|
||||
const value = []
|
||||
let char
|
||||
while (i < n && (char = input[i++]) !== '"') {
|
||||
if (char === '\\') {
|
||||
char = input[i++]
|
||||
}
|
||||
value.push(char)
|
||||
}
|
||||
|
||||
return value.join('')
|
||||
})
|
||||
const parseRawString = () => {
|
||||
let value = ''
|
||||
let c
|
||||
while ((c = input[i]) && RAW_STRING_CHARS[c]) {
|
||||
++i
|
||||
value += c
|
||||
}
|
||||
if (value.length) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
const parseTruthyProperty = backtrace(() => {
|
||||
let name
|
||||
if ((name = parseString()) && parseWs() && input[i++] === '?') {
|
||||
return new TruthyProperty(name)
|
||||
}
|
||||
})
|
||||
|
||||
return input_ => {
|
||||
i = 0
|
||||
input = input_.split('')
|
||||
n = input.length
|
||||
|
||||
try {
|
||||
return parseTerms(And)
|
||||
} finally {
|
||||
input = undefined
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
const _getPropertyClauseStrings = ({ child }) => {
|
||||
if (child instanceof Or) {
|
||||
const strings = []
|
||||
child.children.forEach(child => {
|
||||
if (child instanceof StringNode) {
|
||||
strings.push(child.value)
|
||||
}
|
||||
})
|
||||
return strings
|
||||
}
|
||||
|
||||
if (child instanceof StringNode) {
|
||||
return [child.value]
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
// Find possible values for property clauses in a and clause.
|
||||
export const getPropertyClausesStrings = node => {
|
||||
if (!node) {
|
||||
return {}
|
||||
}
|
||||
|
||||
if (node instanceof Property) {
|
||||
return {
|
||||
[node.name]: _getPropertyClauseStrings(node),
|
||||
}
|
||||
}
|
||||
|
||||
if (node instanceof And) {
|
||||
const strings = {}
|
||||
node.children.forEach(node => {
|
||||
if (node instanceof Property) {
|
||||
const { name } = node
|
||||
const values = strings[name]
|
||||
if (values) {
|
||||
values.push.apply(values, _getPropertyClauseStrings(node))
|
||||
} else {
|
||||
strings[name] = _getPropertyClauseStrings(node)
|
||||
}
|
||||
}
|
||||
})
|
||||
return strings
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export const setPropertyClause = (node, name, child) => {
|
||||
const property = child && new Property(
|
||||
name,
|
||||
typeof child === 'string' ? new StringNode(child) : child
|
||||
)
|
||||
|
||||
if (node === undefined) {
|
||||
return property
|
||||
}
|
||||
|
||||
const children = (node instanceof And ? node.children : [node]).filter(child =>
|
||||
!(child instanceof Property && child.name === name)
|
||||
)
|
||||
if (property !== undefined) {
|
||||
children.push(property)
|
||||
}
|
||||
return new And(children)
|
||||
}
|
||||
77
packages/complex-matcher/src/index.spec.js
Normal file
77
packages/complex-matcher/src/index.spec.js
Normal file
@@ -0,0 +1,77 @@
|
||||
/* eslint-env jest */
|
||||
|
||||
import {
|
||||
getPropertyClausesStrings,
|
||||
Null,
|
||||
parse,
|
||||
setPropertyClause,
|
||||
} from './'
|
||||
import { ast, pattern } from './index.fixtures'
|
||||
|
||||
it('getPropertyClausesStrings', () => {
|
||||
const tmp = getPropertyClausesStrings(parse('foo bar:baz baz:|(foo bar)'))
|
||||
expect(tmp).toEqual({
|
||||
bar: ['baz'],
|
||||
baz: ['foo', 'bar'],
|
||||
})
|
||||
})
|
||||
|
||||
describe('parse', () => {
|
||||
it('analyses a string and returns a node/tree', () => {
|
||||
expect(parse(pattern)).toEqual(ast)
|
||||
})
|
||||
|
||||
it('supports an empty string', () => {
|
||||
expect(parse('')).toEqual(new Null())
|
||||
})
|
||||
|
||||
it('differentiate between numbers and numbers in strings', () => {
|
||||
let node
|
||||
|
||||
node = parse('32')
|
||||
expect(node.match(32)).toBe(true)
|
||||
expect(node.match('32')).toBe(false)
|
||||
expect(node.toString()).toBe('32')
|
||||
|
||||
node = parse('"32"')
|
||||
expect(node.match(32)).toBe(false)
|
||||
expect(node.match('32')).toBe(true)
|
||||
expect(node.toString()).toBe('"32"')
|
||||
})
|
||||
})
|
||||
|
||||
describe('setPropertyClause', () => {
|
||||
it('creates a node if none passed', () => {
|
||||
expect(setPropertyClause(undefined, 'foo', 'bar').toString()).toBe('foo:bar')
|
||||
})
|
||||
|
||||
it('adds a property clause if there was none', () => {
|
||||
expect(
|
||||
setPropertyClause(parse('baz'), 'foo', 'bar').toString()
|
||||
).toBe('baz foo:bar')
|
||||
})
|
||||
|
||||
it('replaces the property clause if there was one', () => {
|
||||
expect(
|
||||
setPropertyClause(parse('plip foo:baz plop'), 'foo', 'bar').toString()
|
||||
).toBe('plip plop foo:bar')
|
||||
|
||||
expect(
|
||||
setPropertyClause(parse('foo:|(baz plop)'), 'foo', 'bar').toString()
|
||||
).toBe('foo:bar')
|
||||
})
|
||||
|
||||
it('removes the property clause if no chid is passed', () => {
|
||||
expect(
|
||||
setPropertyClause(parse('foo bar:baz qux'), 'bar', undefined).toString()
|
||||
).toBe('foo qux')
|
||||
|
||||
expect(
|
||||
setPropertyClause(parse('foo bar:baz qux'), 'baz', undefined).toString()
|
||||
).toBe('foo bar:baz qux')
|
||||
})
|
||||
})
|
||||
|
||||
it('toString', () => {
|
||||
expect(pattern).toBe(ast.toString())
|
||||
})
|
||||
27
packages/value-matcher/.babelrc.js
Normal file
27
packages/value-matcher/.babelrc.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const { NODE_ENV = 'development' } = process.env
|
||||
const __PROD__ = NODE_ENV === 'production'
|
||||
const __TEST__ = NODE_ENV === 'test'
|
||||
|
||||
module.exports = {
|
||||
comments: !__PROD__,
|
||||
compact: __PROD__,
|
||||
ignore: __TEST__ ? undefined : [ /\.spec\.js$/ ],
|
||||
presets: [
|
||||
[
|
||||
'@babel/env',
|
||||
{
|
||||
debug: !__TEST__,
|
||||
loose: true,
|
||||
shippedProposals: true,
|
||||
targets: __PROD__
|
||||
? {
|
||||
browsers: '>2%',
|
||||
node: '4',
|
||||
}
|
||||
: { node: 'current' },
|
||||
useBuiltIns: 'usage',
|
||||
},
|
||||
],
|
||||
'@babel/flow',
|
||||
],
|
||||
}
|
||||
24
packages/value-matcher/.npmignore
Normal file
24
packages/value-matcher/.npmignore
Normal file
@@ -0,0 +1,24 @@
|
||||
/benchmark/
|
||||
/benchmarks/
|
||||
*.bench.js
|
||||
*.bench.js.map
|
||||
|
||||
/examples/
|
||||
example.js
|
||||
example.js.map
|
||||
*.example.js
|
||||
*.example.js.map
|
||||
|
||||
/fixture/
|
||||
/fixtures/
|
||||
*.fixture.js
|
||||
*.fixture.js.map
|
||||
*.fixtures.js
|
||||
*.fixtures.js.map
|
||||
|
||||
/test/
|
||||
/tests/
|
||||
*.spec.js
|
||||
*.spec.js.map
|
||||
|
||||
__snapshots__/
|
||||
66
packages/value-matcher/README.md
Normal file
66
packages/value-matcher/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# value-matcher [](https://travis-ci.org/vatefr/xen-orchestra)
|
||||
|
||||
> ${pkg.description}
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/value-matcher):
|
||||
|
||||
```
|
||||
> npm install --save value-matcher
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { createPredicate } from 'value-matcher'
|
||||
|
||||
[
|
||||
{ user: 'sam', age: 65, active: false },
|
||||
{ user: 'barney', age: 36, active: true },
|
||||
{ user: 'fred', age: 40, active: false },
|
||||
].filter(createPredicate({
|
||||
__or: [
|
||||
{ user: 'sam' },
|
||||
{ active: true },
|
||||
],
|
||||
}))
|
||||
// [
|
||||
// { user: 'sam', age: 65, active: false },
|
||||
// { user: 'barney', age: 36, active: true },
|
||||
// ]
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
```
|
||||
# Install dependencies
|
||||
> yarn
|
||||
|
||||
# Run the tests
|
||||
> yarn test
|
||||
|
||||
# Continuously compile
|
||||
> yarn dev
|
||||
|
||||
# Continuously run the tests
|
||||
> yarn dev-test
|
||||
|
||||
# Build for production (automatically called by npm install)
|
||||
> yarn build
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are *very* welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xo-web/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
ISC © [Vates SAS](https://vates.fr)
|
||||
44
packages/value-matcher/package.json
Normal file
44
packages/value-matcher/package.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "value-matcher",
|
||||
"version": "0.0.0",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/value-matcher",
|
||||
"bugs": "https://github.com/vatesfr/xo-web/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Julien Fontanet",
|
||||
"email": "julien.fontanet@isonoe.net"
|
||||
},
|
||||
"preferGlobal": false,
|
||||
"main": "dist/",
|
||||
"bin": {},
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "7.0.0-beta.37"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.0.0-beta.37",
|
||||
"@babel/core": "7.0.0-beta.37",
|
||||
"@babel/preset-env": "7.0.0-beta.37",
|
||||
"@babel/preset-flow": "7.0.0-beta.37",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
}
|
||||
}
|
||||
63
packages/value-matcher/src/index.js
Normal file
63
packages/value-matcher/src/index.js
Normal file
@@ -0,0 +1,63 @@
|
||||
// @flow
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
export type Pattern = OrPattern | NotPattern | ObjectPattern | ArrayPattern | ValuePattern
|
||||
|
||||
// one of the pattern must match
|
||||
type OrPattern = {| __or: Array<Pattern> |}
|
||||
|
||||
// the pattern must not match
|
||||
type NotPattern = {| __not: Pattern |}
|
||||
|
||||
// value is an object with properties matching the patterns
|
||||
type ObjectPattern = { [string]: Pattern }
|
||||
|
||||
// value is an array and each patterns must match a different item
|
||||
type ArrayPattern = Array<Pattern>
|
||||
|
||||
// value equals the pattern
|
||||
type ValuePattern = bool | number | string
|
||||
|
||||
const match = (pattern: Pattern, value: any) => {
|
||||
if (Array.isArray(pattern)) {
|
||||
return Array.isArray(value) && pattern.every((subpattern, i) =>
|
||||
// FIXME: subpatterns should match different subvalues
|
||||
value.some(subvalue => match(subpattern, subvalue))
|
||||
)
|
||||
}
|
||||
|
||||
if (pattern !== null && typeof pattern === 'object') {
|
||||
const keys = Object.keys(pattern)
|
||||
const { length } = keys
|
||||
|
||||
if (length === 1) {
|
||||
const [ key ] = keys
|
||||
if (key === '__or') {
|
||||
const orPattern: OrPattern = (pattern: any)
|
||||
return orPattern.__or.some(subpattern => match(subpattern, value))
|
||||
}
|
||||
if (key === '__not') {
|
||||
const notPattern: NotPattern = (pattern: any)
|
||||
return !match(notPattern.__not, value)
|
||||
}
|
||||
}
|
||||
|
||||
if (value === null || typeof value !== 'object') {
|
||||
return false
|
||||
}
|
||||
|
||||
const objectPattern: ObjectPattern = (pattern: any)
|
||||
for (let i = 0; i < length; ++i) {
|
||||
const key = keys[i]
|
||||
const subvalue = value[key]
|
||||
if (subvalue === undefined || !match(objectPattern[key], subvalue)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return pattern === value
|
||||
}
|
||||
|
||||
export const createPredicate = (pattern: Pattern) => (value: any) => match(pattern, value)
|
||||
@@ -37,18 +37,15 @@
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.0.1",
|
||||
"dependency-check": "^2.9.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"commitmsg": "npm test",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "dependency-check ./package.json",
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "npm run prebuild",
|
||||
"prepublish": "npm run build"
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
|
||||
@@ -30,7 +30,7 @@ const fuFooter = fu.struct([
|
||||
fu.uint32('fileFormatVersion'), // 12
|
||||
fu.struct('dataOffset', [
|
||||
fu.uint32('high'), // 16
|
||||
fu.uint32('low') // 20
|
||||
fu.uint32('low'), // 20
|
||||
]),
|
||||
fu.uint32('timestamp'), // 24
|
||||
fu.char('creatorApplication', 4), // 28
|
||||
@@ -38,23 +38,23 @@ const fuFooter = fu.struct([
|
||||
fu.uint32('creatorHostOs'), // 36
|
||||
fu.struct('originalSize', [ // At the creation, current size of the hard disk.
|
||||
fu.uint32('high'), // 40
|
||||
fu.uint32('low') // 44
|
||||
fu.uint32('low'), // 44
|
||||
]),
|
||||
fu.struct('currentSize', [ // Current size of the virtual disk. At the creation: currentSize = originalSize.
|
||||
fu.uint32('high'), // 48
|
||||
fu.uint32('low') // 52
|
||||
fu.uint32('low'), // 52
|
||||
]),
|
||||
fu.struct('diskGeometry', [
|
||||
fu.uint16('cylinders'), // 56
|
||||
fu.uint8('heads'), // 58
|
||||
fu.uint8('sectorsPerTrackCylinder') // 59
|
||||
fu.uint8('sectorsPerTrackCylinder'), // 59
|
||||
]),
|
||||
fu.uint32('diskType'), // 60 Disk type, must be equal to HARD_DISK_TYPE_DYNAMIC/HARD_DISK_TYPE_DIFFERENCING.
|
||||
fu.uint32('checksum'), // 64
|
||||
fu.uint8('uuid', 16), // 68
|
||||
fu.char('saved'), // 84
|
||||
fu.char('hidden'), // 85
|
||||
fu.byte('reserved', 426) // 86
|
||||
fu.byte('reserved', 426), // 86
|
||||
])
|
||||
const FOOTER_SIZE = fuFooter.size
|
||||
|
||||
@@ -62,11 +62,11 @@ const fuHeader = fu.struct([
|
||||
fu.char('cookie', 8),
|
||||
fu.struct('dataOffset', [
|
||||
fu.uint32('high'),
|
||||
fu.uint32('low')
|
||||
fu.uint32('low'),
|
||||
]),
|
||||
fu.struct('tableOffset', [ // Absolute byte offset of the Block Allocation Table.
|
||||
fu.uint32('high'),
|
||||
fu.uint32('low')
|
||||
fu.uint32('low'),
|
||||
]),
|
||||
fu.uint32('headerVersion'),
|
||||
fu.uint32('maxTableEntries'), // Max entries in the Block Allocation Table.
|
||||
@@ -83,10 +83,10 @@ const fuHeader = fu.struct([
|
||||
fu.uint32('reserved'),
|
||||
fu.struct('platformDataOffset', [ // Absolute byte offset of the locator data.
|
||||
fu.uint32('high'),
|
||||
fu.uint32('low')
|
||||
])
|
||||
fu.uint32('low'),
|
||||
]),
|
||||
], 8),
|
||||
fu.byte('reserved2', 256)
|
||||
fu.byte('reserved2', 256),
|
||||
])
|
||||
const HEADER_SIZE = fuHeader.size
|
||||
|
||||
@@ -265,7 +265,7 @@ export default class Vhd {
|
||||
|
||||
return this._handler.createReadStream(this._path, {
|
||||
end: begin + length - 1,
|
||||
start: begin
|
||||
start: begin,
|
||||
}).then(buf
|
||||
? stream => streamToExistingBuffer(stream, buf, offset, (offset || 0) + length)
|
||||
: streamToNewBuffer
|
||||
@@ -324,7 +324,7 @@ export default class Vhd {
|
||||
const sectorsPerBlock = header.blockSize / SECTOR_SIZE
|
||||
assert(sectorsPerBlock % 1 === 0)
|
||||
|
||||
// 1 bit per sector, rounded up to full sectors
|
||||
// 1 bit per sector, rounded up to full sectors
|
||||
this._blockBitmapSize = Math.ceil(sectorsPerBlock / 8 / SECTOR_SIZE) * SECTOR_SIZE
|
||||
assert(this._blockBitmapSize === SECTOR_SIZE)
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- stable
|
||||
- 6
|
||||
- 4
|
||||
|
||||
# Use containers.
|
||||
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
||||
sudo: false
|
||||
@@ -1,7 +1,7 @@
|
||||
const { createReadStream, createWriteStream, statSync } = require('fs')
|
||||
const { PassThrough } = require('stream')
|
||||
|
||||
const { isOpaqueRef } = require('../')
|
||||
const { isOpaqueRef } = require('../') // eslint-disable-line node/no-missing-require
|
||||
|
||||
exports.createInputStream = path => {
|
||||
if (path === undefined || path === '-') {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xen-api",
|
||||
"version": "0.15.1",
|
||||
"version": "0.16.3",
|
||||
"license": "ISC",
|
||||
"description": "Connector to the Xen API",
|
||||
"keywords": [
|
||||
@@ -34,7 +34,7 @@
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"blocked": "^1.2.1",
|
||||
"debug": "^2.6.8",
|
||||
"debug": "^3.1.0",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"http-request-plus": "^0.5.0",
|
||||
@@ -44,7 +44,7 @@
|
||||
"lodash": "^4.17.4",
|
||||
"make-error": "^1.3.0",
|
||||
"minimist": "^1.2.0",
|
||||
"ms": "^2.0.0",
|
||||
"ms": "^2.1.1",
|
||||
"promise-toolbox": "^0.9.5",
|
||||
"pw": "0.0.4",
|
||||
"xmlrpc": "^1.3.2",
|
||||
@@ -54,26 +54,25 @@
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||
"babel-plugin-transform-function-bind": "^6.22.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"cross-env": "^5.0.5",
|
||||
"dependency-check": "^2.9.1",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"commitmsg": "npm test",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"plot": "gnuplot -p memory-test.gnu",
|
||||
"posttest": "dependency-check ./package.json",
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "npm run prebuild",
|
||||
"prepublish": "npm run build"
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"lodash",
|
||||
"transform-decorators-legacy"
|
||||
"transform-decorators-legacy",
|
||||
"transform-function-bind"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
@@ -84,7 +83,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-0"
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ const main = async args => {
|
||||
debounce: 'd',
|
||||
help: 'h',
|
||||
'read-only': 'ro',
|
||||
verbose: 'v'
|
||||
}
|
||||
verbose: 'v',
|
||||
},
|
||||
})
|
||||
|
||||
if (opts.help) {
|
||||
@@ -72,12 +72,12 @@ const main = async args => {
|
||||
allowUnauthorized: opts.au,
|
||||
auth,
|
||||
debounce: opts.debounce != null ? +opts.debounce : null,
|
||||
readOnly: opts.ro
|
||||
readOnly: opts.ro,
|
||||
})
|
||||
await xapi.connect()
|
||||
|
||||
const repl = createRepl({
|
||||
prompt: `${xapi._humanId}> `
|
||||
prompt: `${xapi._humanId}> `,
|
||||
})
|
||||
repl.context.xapi = xapi
|
||||
|
||||
|
||||
@@ -6,7 +6,18 @@ import httpRequest from 'http-request-plus'
|
||||
import { BaseError } from 'make-error'
|
||||
import { EventEmitter } from 'events'
|
||||
import { fibonacci } from 'iterable-backoff'
|
||||
import { filter, forEach, isArray, isObject, map, noop, omit, reduce, startsWith } from 'lodash'
|
||||
import {
|
||||
filter,
|
||||
forEach,
|
||||
isArray,
|
||||
isInteger,
|
||||
isObject,
|
||||
map,
|
||||
noop,
|
||||
omit,
|
||||
reduce,
|
||||
startsWith,
|
||||
} from 'lodash'
|
||||
import {
|
||||
Cancel,
|
||||
cancelable,
|
||||
@@ -14,7 +25,7 @@ import {
|
||||
defer,
|
||||
delay as pDelay,
|
||||
fromEvents,
|
||||
lastly
|
||||
lastly,
|
||||
} from 'promise-toolbox'
|
||||
|
||||
import autoTransport from './transports/auto'
|
||||
@@ -41,7 +52,7 @@ const NETWORK_ERRORS = {
|
||||
EHOSTUNREACH: true,
|
||||
|
||||
// Connection configured timed out has been reach.
|
||||
ETIMEDOUT: true
|
||||
ETIMEDOUT: true,
|
||||
}
|
||||
|
||||
const isNetworkError = ({code}) => NETWORK_ERRORS[code]
|
||||
@@ -50,7 +61,7 @@ const isNetworkError = ({code}) => NETWORK_ERRORS[code]
|
||||
|
||||
const XAPI_NETWORK_ERRORS = {
|
||||
HOST_STILL_BOOTING: true,
|
||||
HOST_HAS_NO_MANAGEMENT_IP: true
|
||||
HOST_HAS_NO_MANAGEMENT_IP: true,
|
||||
}
|
||||
|
||||
const isXapiNetworkError = ({code}) => XAPI_NETWORK_ERRORS[code]
|
||||
@@ -68,19 +79,28 @@ const isSessionInvalid = ({code}) => code === 'SESSION_INVALID'
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class XapiError extends BaseError {
|
||||
constructor ([code, ...params]) {
|
||||
constructor (code, params) {
|
||||
super(`${code}(${params.join(', ')})`)
|
||||
|
||||
this.code = code
|
||||
this.params = params
|
||||
|
||||
// slot than can be assigned later
|
||||
// slots than can be assigned later
|
||||
this.method = undefined
|
||||
this.url = undefined
|
||||
}
|
||||
}
|
||||
|
||||
export const wrapError = error => new XapiError(error)
|
||||
export const wrapError = error => {
|
||||
let code, params
|
||||
if (isArray(error)) { // < XenServer 7.3
|
||||
[ code, ...params ] = error
|
||||
} else {
|
||||
code = error.message
|
||||
params = error.data
|
||||
}
|
||||
return new XapiError(code, params)
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -101,7 +121,7 @@ const {
|
||||
create: createObject,
|
||||
defineProperties,
|
||||
defineProperty,
|
||||
freeze: freezeObject
|
||||
freeze: freezeObject,
|
||||
} = Object
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
@@ -120,6 +140,31 @@ const isReadOnlyCall = (method, args) => (
|
||||
RE_READ_ONLY_METHOD.test(method)
|
||||
)
|
||||
|
||||
// Prepare values before passing them to the XenAPI:
|
||||
//
|
||||
// - cast integers to strings
|
||||
const prepareParam = param => {
|
||||
if (isInteger(param)) {
|
||||
return String(param)
|
||||
}
|
||||
|
||||
if (typeof param !== 'object' || param === null) {
|
||||
return param
|
||||
}
|
||||
|
||||
if (isArray(param)) {
|
||||
return map(param, prepareParam)
|
||||
}
|
||||
|
||||
const values = {}
|
||||
forEach(param, (value, key) => {
|
||||
if (value !== undefined) {
|
||||
values[key] = prepareParam(value)
|
||||
}
|
||||
})
|
||||
return values
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
const getKey = o => o.$id
|
||||
@@ -171,7 +216,7 @@ export class Xapi extends EventEmitter {
|
||||
if (user !== undefined) {
|
||||
this._auth = {
|
||||
user,
|
||||
password: url.password
|
||||
password: url.password,
|
||||
}
|
||||
delete url.username
|
||||
delete url.password
|
||||
@@ -190,6 +235,8 @@ export class Xapi extends EventEmitter {
|
||||
// Memoize this function _addObject().
|
||||
this._getPool = () => this._pool
|
||||
|
||||
this._nTasks = 0
|
||||
|
||||
const objects = this._objects = new Collection()
|
||||
objects.getKey = getKey
|
||||
|
||||
@@ -214,7 +261,7 @@ export class Xapi extends EventEmitter {
|
||||
this.__url = url
|
||||
this._call = autoTransport({
|
||||
allowUnauthorized: this._allowUnauthorized,
|
||||
url
|
||||
url,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -306,7 +353,7 @@ export class Xapi extends EventEmitter {
|
||||
|
||||
return this._transportCall('session.login_with_password', [
|
||||
auth.user,
|
||||
auth.password
|
||||
auth.password,
|
||||
]).then(
|
||||
sessionId => {
|
||||
this._sessionId = sessionId
|
||||
@@ -331,6 +378,8 @@ export class Xapi extends EventEmitter {
|
||||
return Promise.reject(new Error('already disconnected'))
|
||||
}
|
||||
|
||||
this._transportCall('session.logout', [ this._sessionId ]).catch(noop)
|
||||
|
||||
this._sessionId = null
|
||||
|
||||
debug('%s: disconnected', this._humanId)
|
||||
@@ -343,27 +392,33 @@ export class Xapi extends EventEmitter {
|
||||
call (method, ...args) {
|
||||
return this._readOnly && !isReadOnlyCall(method, args)
|
||||
? Promise.reject(new Error(`cannot call ${method}() in read only mode`))
|
||||
: this._sessionCall(method, args)
|
||||
: this._sessionCall(method, prepareParam(args))
|
||||
}
|
||||
|
||||
@cancelable
|
||||
callAsync ($cancelToken, method, ...args) {
|
||||
return this.call(`Async.${method}`, ...args).then(taskRef => {
|
||||
$cancelToken.promise.then(() => {
|
||||
this._sessionCall('task.cancel', taskRef).catch(noop)
|
||||
})
|
||||
return this._readOnly && !isReadOnlyCall(method, args)
|
||||
? Promise.reject(new Error(`cannot call ${method}() in read only mode`))
|
||||
: this._sessionCall(`Async.${method}`, ...args).then(taskRef => {
|
||||
$cancelToken.promise.then(() => {
|
||||
this._sessionCall('task.cancel', taskRef).catch(noop)
|
||||
})
|
||||
|
||||
return this.watchTask(taskRef)::lastly(() => {
|
||||
this._sessionCall('task.destroy', taskRef).catch(noop)
|
||||
return this.watchTask(taskRef)::lastly(() => {
|
||||
this._sessionCall('task.destroy', taskRef).catch(noop)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// create a task and automatically destroy it when settled
|
||||
createTask (nameLabel, nameDescription = '') {
|
||||
if (this._readOnly) {
|
||||
return Promise.reject(new Error('cannot create task in read only mode'))
|
||||
}
|
||||
|
||||
const promise = this._sessionCall('task.create', [
|
||||
nameLabel,
|
||||
nameDescription
|
||||
nameDescription,
|
||||
])
|
||||
|
||||
promise.then(taskRef => {
|
||||
@@ -419,11 +474,15 @@ export class Xapi extends EventEmitter {
|
||||
throw new Error('there is no object with the UUID ' + uuid)
|
||||
}
|
||||
|
||||
getRecord (type, ref) {
|
||||
return this._sessionCall(`${type}.get_record`, ref)
|
||||
}
|
||||
|
||||
@cancelable
|
||||
getResource ($cancelToken, pathname, {
|
||||
host,
|
||||
query,
|
||||
task
|
||||
task,
|
||||
}) {
|
||||
return this._autoTask(
|
||||
task,
|
||||
@@ -444,12 +503,12 @@ export class Xapi extends EventEmitter {
|
||||
$cancelToken,
|
||||
this._url,
|
||||
host && {
|
||||
hostname: this.getObject(host).address
|
||||
hostname: this.getObject(host).address,
|
||||
},
|
||||
{
|
||||
pathname,
|
||||
query,
|
||||
rejectUnauthorized: !this._allowUnauthorized
|
||||
rejectUnauthorized: !this._allowUnauthorized,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -468,8 +527,12 @@ export class Xapi extends EventEmitter {
|
||||
putResource ($cancelToken, body, pathname, {
|
||||
host,
|
||||
query,
|
||||
task
|
||||
task,
|
||||
} = {}) {
|
||||
if (this._readOnly) {
|
||||
return Promise.reject(new Error(new Error('cannot put resource in read only mode')))
|
||||
}
|
||||
|
||||
return this._autoTask(
|
||||
task,
|
||||
`Xapi#putResource ${pathname}`
|
||||
@@ -500,14 +563,14 @@ export class Xapi extends EventEmitter {
|
||||
$cancelToken,
|
||||
this._url,
|
||||
host && {
|
||||
hostname: this.getObject(host).address
|
||||
hostname: this.getObject(host).address,
|
||||
},
|
||||
{
|
||||
body,
|
||||
headers,
|
||||
pathname,
|
||||
query,
|
||||
rejectUnauthorized: !this._allowUnauthorized
|
||||
rejectUnauthorized: !this._allowUnauthorized,
|
||||
},
|
||||
override
|
||||
)
|
||||
@@ -523,7 +586,7 @@ export class Xapi extends EventEmitter {
|
||||
? omit(query, 'task_id')
|
||||
: query,
|
||||
|
||||
maxRedirects: 0
|
||||
maxRedirects: 0,
|
||||
}).then(
|
||||
response => {
|
||||
response.req.abort()
|
||||
@@ -645,7 +708,7 @@ export class Xapi extends EventEmitter {
|
||||
id: true,
|
||||
pool: true,
|
||||
ref: true,
|
||||
type: true
|
||||
type: true,
|
||||
}
|
||||
const getKey = (key, obj) => reservedKeys[key] && obj === object
|
||||
? `$$${key}`
|
||||
@@ -659,7 +722,7 @@ export class Xapi extends EventEmitter {
|
||||
// it is not supposed to contain links, therefore, in
|
||||
// benefice of the doubt, a resolved property is defined.
|
||||
defineProperty(object, getKey(key, object), {
|
||||
value: EMPTY_ARRAY
|
||||
value: EMPTY_ARRAY,
|
||||
})
|
||||
|
||||
// Minor memory optimization, use the same empty array for
|
||||
@@ -668,7 +731,7 @@ export class Xapi extends EventEmitter {
|
||||
} else if (isOpaqueRef(value[0])) {
|
||||
// This is an array of refs.
|
||||
defineProperty(object, getKey(key, object), {
|
||||
get: () => freezeObject(map(value, (ref) => objectsByRefs[ref]))
|
||||
get: () => freezeObject(map(value, (ref) => objectsByRefs[ref])),
|
||||
})
|
||||
|
||||
freezeObject(value)
|
||||
@@ -679,7 +742,7 @@ export class Xapi extends EventEmitter {
|
||||
freezeObject(value)
|
||||
} else if (isOpaqueRef(value)) {
|
||||
defineProperty(object, getKey(key, object), {
|
||||
get: () => objectsByRefs[value]
|
||||
get: () => objectsByRefs[value],
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -689,7 +752,7 @@ export class Xapi extends EventEmitter {
|
||||
$id: { value: object.uuid || ref },
|
||||
$pool: { get: this._getPool },
|
||||
$ref: { value: ref },
|
||||
$type: { value: type }
|
||||
$type: { value: type },
|
||||
})
|
||||
|
||||
// Finally freezes the object.
|
||||
@@ -709,9 +772,24 @@ export class Xapi extends EventEmitter {
|
||||
|
||||
if (type === 'pool') {
|
||||
this._pool = object
|
||||
|
||||
const eventWatchers = this._eventWatchers
|
||||
if (eventWatchers !== undefined) {
|
||||
forEach(object.other_config, (_, key) => {
|
||||
const eventWatcher = eventWatchers[key]
|
||||
if (eventWatcher !== undefined) {
|
||||
delete eventWatchers[key]
|
||||
eventWatcher(object)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else if (type === 'task') {
|
||||
if (prev === undefined) {
|
||||
++this._nTasks
|
||||
}
|
||||
|
||||
const taskWatchers = this._taskWatchers
|
||||
let taskWatcher = taskWatchers[ref]
|
||||
const taskWatcher = taskWatchers[ref]
|
||||
if (
|
||||
taskWatcher !== undefined &&
|
||||
getTaskResult(object, taskWatcher.resolve, taskWatcher.reject)
|
||||
@@ -719,16 +797,18 @@ export class Xapi extends EventEmitter {
|
||||
delete taskWatchers[ref]
|
||||
}
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
_removeObject (ref) {
|
||||
_removeObject (type, ref) {
|
||||
const byRefs = this._objectsByRefs
|
||||
const object = byRefs[ref]
|
||||
if (object !== undefined) {
|
||||
this._objects.unset(object.$id)
|
||||
delete byRefs[ref]
|
||||
|
||||
if (type === 'task') {
|
||||
--this._nTasks
|
||||
}
|
||||
}
|
||||
|
||||
const taskWatchers = this._taskWatchers
|
||||
@@ -740,26 +820,12 @@ export class Xapi extends EventEmitter {
|
||||
}
|
||||
|
||||
_processEvents (events) {
|
||||
const eventWatchers = this._eventWatchers
|
||||
|
||||
forEach(events, event => {
|
||||
let object
|
||||
const { ref } = event
|
||||
const { class: type, ref } = event
|
||||
if (event.operation === 'del') {
|
||||
this._removeObject(ref)
|
||||
this._removeObject(type, ref)
|
||||
} else {
|
||||
const type = event.class
|
||||
object = this._addObject(type, ref, event.snapshot)
|
||||
|
||||
if (eventWatchers !== undefined && type === 'pool') {
|
||||
forEach(object.other_config, (_, key) => {
|
||||
const eventWatcher = eventWatchers[key]
|
||||
if (eventWatcher !== undefined) {
|
||||
delete eventWatchers[key]
|
||||
eventWatcher(object)
|
||||
}
|
||||
})
|
||||
}
|
||||
this._addObject(type, ref, event.snapshot)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -768,13 +834,27 @@ export class Xapi extends EventEmitter {
|
||||
const loop = () => this.status === CONNECTED && this._sessionCall('event.from', [
|
||||
['*'],
|
||||
this._fromToken,
|
||||
60 + 0.1 // Force float.
|
||||
60 + 0.1, // Force float.
|
||||
]).then(onSuccess, onFailure)
|
||||
|
||||
const onSuccess = ({token, events}) => {
|
||||
const onSuccess = ({ events, token, valid_ref_counts: { task } }) => {
|
||||
this._fromToken = token
|
||||
this._processEvents(events)
|
||||
|
||||
if (task !== this._nTasks) {
|
||||
forEach(this.objects.all, object => {
|
||||
if (object.$type === 'task') {
|
||||
this._removeObject('task', object.$ref)
|
||||
}
|
||||
})
|
||||
|
||||
this._sessionCall('task.get_all_records').then(tasks => {
|
||||
forEach(tasks, (task, ref) => {
|
||||
this._addObject('task', ref, task)
|
||||
})
|
||||
}).catch(noop)
|
||||
}
|
||||
|
||||
const debounce = this._debounce
|
||||
return debounce != null
|
||||
? pDelay(debounce).then(loop)
|
||||
@@ -864,7 +944,7 @@ export class Xapi extends EventEmitter {
|
||||
Xapi.prototype._transportCall = reduce([
|
||||
function (method, args) {
|
||||
return this._call(method, args).catch(error => {
|
||||
if (isArray(error)) {
|
||||
if (!(error instanceof Error)) {
|
||||
error = wrapError(error)
|
||||
}
|
||||
|
||||
@@ -906,7 +986,7 @@ Xapi.prototype._transportCall = reduce([
|
||||
|
||||
const newUrl = {
|
||||
...this._url,
|
||||
hostname: master
|
||||
hostname: master,
|
||||
}
|
||||
this.emit('redirect', newUrl)
|
||||
this._url = newUrl
|
||||
@@ -938,7 +1018,7 @@ Xapi.prototype._transportCall = reduce([
|
||||
throw error
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
], (call, decorator) => decorator(call))
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -10,7 +10,7 @@ const xapi = (() => {
|
||||
return createClient({
|
||||
auth: { user, password },
|
||||
url,
|
||||
watchEvents: false
|
||||
watchEvents: false,
|
||||
})
|
||||
})()
|
||||
|
||||
|
||||
@@ -18,5 +18,5 @@ const [ , , url, user, password ] = process.argv
|
||||
createClient({
|
||||
auth: { user, password },
|
||||
readOnly: true,
|
||||
url
|
||||
url,
|
||||
}).connect()
|
||||
|
||||
@@ -9,9 +9,9 @@ export default ({ allowUnauthorized, url }) => {
|
||||
body: format.request(0, method, args),
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
path: '/jsonrpc'
|
||||
path: '/jsonrpc',
|
||||
}).readAll('utf8').then(
|
||||
text => {
|
||||
let response
|
||||
|
||||
@@ -18,7 +18,7 @@ const logError = error => {
|
||||
|
||||
const SPECIAL_CHARS = {
|
||||
'\r': '\\r',
|
||||
'\t': '\\t'
|
||||
'\t': '\\t',
|
||||
}
|
||||
const SPECIAL_CHARS_RE = new RegExp(
|
||||
Object.keys(SPECIAL_CHARS).join('|'),
|
||||
@@ -76,7 +76,7 @@ const parseResult = result => {
|
||||
|
||||
export default ({
|
||||
allowUnauthorized,
|
||||
url: { hostname, path, port, protocol }
|
||||
url: { hostname, path, port, protocol },
|
||||
}) => {
|
||||
const client = (
|
||||
protocol === 'https:'
|
||||
@@ -86,7 +86,7 @@ export default ({
|
||||
host: hostname,
|
||||
path: '/json',
|
||||
port,
|
||||
rejectUnauthorized: !allowUnauthorized
|
||||
rejectUnauthorized: !allowUnauthorized,
|
||||
})
|
||||
const call = promisify(client.methodCall, client)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ const parseResult = result => {
|
||||
|
||||
export default ({
|
||||
allowUnauthorized,
|
||||
url: { hostname, path, port, protocol }
|
||||
url: { hostname, path, port, protocol },
|
||||
}) => {
|
||||
const client = (
|
||||
protocol === 'https:'
|
||||
@@ -41,7 +41,7 @@ export default ({
|
||||
)({
|
||||
host: hostname,
|
||||
port,
|
||||
rejectUnauthorized: !allowUnauthorized
|
||||
rejectUnauthorized: !allowUnauthorized,
|
||||
})
|
||||
const call = promisify(client.methodCall, client)
|
||||
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
"comments": false,
|
||||
"compact": true,
|
||||
"presets": [
|
||||
"stage-0",
|
||||
"es2015"
|
||||
[
|
||||
"env", {
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -25,15 +25,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"dependency-check": "^2.9.1"
|
||||
"babel-preset-env": "^1.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel --source-maps --out-dir=dist/ src/",
|
||||
"depcheck": "dependency-check ./package.json",
|
||||
"dev": "babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "yarn run depcheck",
|
||||
"prepublish": "yarn run build"
|
||||
"prepublishOnly": "yarn run build"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ const checkAuthorizationByTypes = {
|
||||
|
||||
'VM-snapshot': checkMember('$snapshot_of'),
|
||||
|
||||
'VM-template': or(checkSelf, checkMember('$pool'))
|
||||
'VM-template': or(checkSelf, checkMember('$pool')),
|
||||
}
|
||||
|
||||
// Hoisting is important for this function.
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
const { NODE_ENV = 'development' } = process.env
|
||||
const __PROD__ = NODE_ENV === 'production'
|
||||
const __TEST__ = NODE_ENV === 'test'
|
||||
|
||||
module.exports = {
|
||||
comments: false,
|
||||
compact: true,
|
||||
ignore: NODE_ENV === 'test' ? undefined : ['*.spec.js'],
|
||||
// plugins: ['lodash']
|
||||
comments: !__PROD__,
|
||||
compact: __PROD__,
|
||||
ignore: __TEST__ ? undefined : [ /\.spec\.js$/ ],
|
||||
plugins: ['lodash'],
|
||||
presets: [
|
||||
[
|
||||
'env',
|
||||
'@babel/env',
|
||||
{
|
||||
debug: true,
|
||||
debug: !__TEST__,
|
||||
loose: true,
|
||||
targets: {
|
||||
node: process.env.NODE_ENV === 'production' ? '6' : 'current'
|
||||
},
|
||||
useBuiltIns: 'usage'
|
||||
}
|
||||
shippedProposals: true,
|
||||
targets: __PROD__
|
||||
? { node: '6' }
|
||||
: { node: 'current' },
|
||||
useBuiltIns: 'usage',
|
||||
},
|
||||
],
|
||||
'flow'
|
||||
]
|
||||
'@babel/flow',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- stable
|
||||
- 6
|
||||
|
||||
# Use containers.
|
||||
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
||||
sudo: false
|
||||
@@ -134,9 +134,6 @@ encoding by prefixing with `json:`:
|
||||
|
||||
# Build for production (automatically called by npm install)
|
||||
> yarn build
|
||||
|
||||
# Commit changes
|
||||
> yarn cz
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-cli",
|
||||
"version": "0.10.0",
|
||||
"version": "0.10.1",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "Basic CLI for Xen-Orchestra",
|
||||
"keywords": [
|
||||
@@ -28,14 +28,14 @@
|
||||
"node": ">=6"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^7.0.0-beta.3",
|
||||
"@babel/polyfill": "7.0.0-beta.37",
|
||||
"bluebird": "^3.5.1",
|
||||
"chalk": "^2.2.0",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"fs-promise": "^2.0.3",
|
||||
"got": "^7.1.0",
|
||||
"human-format": "^0.9.0",
|
||||
"got": "^8.0.1",
|
||||
"human-format": "^0.9.2",
|
||||
"l33teral": "^3.0.3",
|
||||
"lodash": "^4.17.4",
|
||||
"micromatch": "^3.1.3",
|
||||
@@ -49,28 +49,20 @@
|
||||
"xo-lib": "^0.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^7.0.0-beta.3",
|
||||
"@babel/cli": "7.0.0-beta.37",
|
||||
"@babel/core": "7.0.0-beta.37",
|
||||
"@babel/preset-env": "7.0.0-beta.37",
|
||||
"@babel/preset-flow": "7.0.0-beta.37",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-preset-env": "^7.0.0-beta.3",
|
||||
"babel-preset-flow": "^7.0.0-beta.3",
|
||||
"cross-env": "^5.1.0",
|
||||
"dependency-check": "^2.9.1",
|
||||
"flow-bin": "^0.59.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"commitmsg": "npm test",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "dependency-check ./package.json",
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "npm run prebuild",
|
||||
"prepublish": "npm run build",
|
||||
"prepublishOnly": "npm run build",
|
||||
"pretest": "flow status"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,24 @@
|
||||
|
||||
// ===================================================================
|
||||
|
||||
var promisify = require('bluebird').promisify
|
||||
const promisify = require('bluebird').promisify
|
||||
|
||||
var readFile = promisify(require('fs').readFile)
|
||||
var writeFile = promisify(require('fs').writeFile)
|
||||
const readFile = promisify(require('fs').readFile)
|
||||
const writeFile = promisify(require('fs').writeFile)
|
||||
|
||||
var assign = require('lodash/assign')
|
||||
var l33t = require('l33teral')
|
||||
var mkdirp = promisify(require('mkdirp'))
|
||||
var xdgBasedir = require('xdg-basedir')
|
||||
const assign = require('lodash/assign')
|
||||
const l33t = require('l33teral')
|
||||
const mkdirp = promisify(require('mkdirp'))
|
||||
const xdgBasedir = require('xdg-basedir')
|
||||
|
||||
// ===================================================================
|
||||
|
||||
var configPath = xdgBasedir.config + '/xo-cli'
|
||||
var configFile = configPath + '/config.json'
|
||||
const configPath = xdgBasedir.config + '/xo-cli'
|
||||
const configFile = configPath + '/config.json'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
var load = exports.load = function () {
|
||||
const load = exports.load = function () {
|
||||
return readFile(configFile).then(JSON.parse).catch(function () {
|
||||
return {}
|
||||
})
|
||||
@@ -31,7 +31,7 @@ exports.get = function (path) {
|
||||
})
|
||||
}
|
||||
|
||||
var save = exports.save = function (config) {
|
||||
const save = exports.save = function (config) {
|
||||
return mkdirp(configPath).then(function () {
|
||||
return writeFile(configFile, JSON.stringify(config))
|
||||
})
|
||||
@@ -45,7 +45,7 @@ exports.set = function (data) {
|
||||
|
||||
exports.unset = function (paths) {
|
||||
return load().then(function (config) {
|
||||
var l33tConfig = l33t(config)
|
||||
const l33tConfig = l33t(config)
|
||||
;[].concat(paths).forEach(function (path) {
|
||||
l33tConfig.purge(path, true)
|
||||
})
|
||||
|
||||
@@ -27,6 +27,9 @@ const startsWith = require('lodash/startsWith')
|
||||
const prettyMs = require('pretty-ms')
|
||||
const progressStream = require('progress-stream')
|
||||
const pw = require('pw')
|
||||
|
||||
// FIXME: re-enable the rule when https://github.com/mysticatea/eslint-plugin-node/issues/100 is fixed
|
||||
// eslint-disable-next-line node/no-missing-require
|
||||
const Xo = require('xo-lib').default
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
@@ -103,7 +106,7 @@ function parseParameters (args) {
|
||||
|
||||
const humanFormatOpts = {
|
||||
unit: 'B',
|
||||
scale: 'binary'
|
||||
scale: 'binary',
|
||||
}
|
||||
|
||||
function printProgress (progress) {
|
||||
@@ -214,7 +217,7 @@ async function register (args) {
|
||||
password = await new Promise(function (resolve) {
|
||||
process.stdout.write('Password: ')
|
||||
pw(resolve)
|
||||
})
|
||||
}),
|
||||
] = args
|
||||
|
||||
const xo = new Xo({ url })
|
||||
@@ -224,7 +227,7 @@ async function register (args) {
|
||||
|
||||
await config.set({
|
||||
server: url,
|
||||
token: await xo.call('token.create', { expiresIn })
|
||||
token: await xo.call('token.create', { expiresIn }),
|
||||
})
|
||||
}
|
||||
exports.register = register
|
||||
@@ -232,7 +235,7 @@ exports.register = register
|
||||
function unregister () {
|
||||
return config.unset([
|
||||
'server',
|
||||
'token'
|
||||
'token',
|
||||
])
|
||||
}
|
||||
exports.unregister = unregister
|
||||
@@ -367,7 +370,7 @@ async function call (args) {
|
||||
}
|
||||
}),
|
||||
progress,
|
||||
output
|
||||
output,
|
||||
]), 'finish')
|
||||
}
|
||||
|
||||
@@ -381,16 +384,16 @@ async function call (args) {
|
||||
createReadStream(file),
|
||||
progressStream({
|
||||
length: length,
|
||||
time: 1e3
|
||||
}, printProgress)
|
||||
time: 1e3,
|
||||
}, printProgress),
|
||||
])
|
||||
|
||||
const response = await got.post(url, {
|
||||
body: input,
|
||||
headers: {
|
||||
'content-length': length
|
||||
'content-length': length,
|
||||
},
|
||||
method: 'POST'
|
||||
method: 'POST',
|
||||
})
|
||||
return response.body
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = require('./dist/index')
|
||||
module.exports = require('./dist/index') // eslint-disable-line node/no-missing-require
|
||||
|
||||
@@ -36,18 +36,17 @@
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.0.1",
|
||||
"dependency-check": "^2.9.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"lint-staged": "^6.0.0",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "dependency-check ./package.json --entry dist/collection.js index.js unique-index.js view.js",
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublish": "yarn run build"
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
|
||||
@@ -10,7 +10,7 @@ import isObject from './is-object'
|
||||
|
||||
const {
|
||||
create: createObject,
|
||||
prototype: { hasOwnProperty }
|
||||
prototype: { hasOwnProperty },
|
||||
} = Object
|
||||
|
||||
export const ACTION_ADD = 'add'
|
||||
@@ -269,7 +269,7 @@ export default class Collection extends EventEmitter {
|
||||
const data = {
|
||||
add: createObject(null),
|
||||
remove: createObject(null),
|
||||
update: createObject(null)
|
||||
update: createObject(null),
|
||||
}
|
||||
|
||||
for (const key in this._buffer) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import eventToPromise from 'event-to-promise'
|
||||
import { forEach } from 'lodash'
|
||||
|
||||
import Collection, {DuplicateItem, NoSuchItem} from '..'
|
||||
import Collection, {DuplicateItem, NoSuchItem} from './collection'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -261,58 +261,58 @@ describe('Collection', function () {
|
||||
'add & update → add': [
|
||||
[
|
||||
['add', 'foo', 0],
|
||||
['update', 'foo', 1]
|
||||
['update', 'foo', 1],
|
||||
],
|
||||
{
|
||||
add: {
|
||||
foo: 1
|
||||
}
|
||||
}
|
||||
foo: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
'add & remove → ∅': [
|
||||
[
|
||||
['add', 'foo', 0],
|
||||
['remove', 'foo']
|
||||
['remove', 'foo'],
|
||||
],
|
||||
{}
|
||||
{},
|
||||
],
|
||||
|
||||
'update & update → update': [
|
||||
[
|
||||
['update', 'bar', 1],
|
||||
['update', 'bar', 2]
|
||||
['update', 'bar', 2],
|
||||
],
|
||||
{
|
||||
update: {
|
||||
bar: 2
|
||||
}
|
||||
}
|
||||
bar: 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
'update & remove → remove': [
|
||||
[
|
||||
['update', 'bar', 1],
|
||||
['remove', 'bar']
|
||||
['remove', 'bar'],
|
||||
],
|
||||
{
|
||||
remove: {
|
||||
bar: undefined
|
||||
}
|
||||
}
|
||||
bar: undefined,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
'remove & add → update': [
|
||||
[
|
||||
['remove', 'bar'],
|
||||
['add', 'bar', 0]
|
||||
['add', 'bar', 0],
|
||||
],
|
||||
{
|
||||
update: {
|
||||
bar: 0
|
||||
}
|
||||
}
|
||||
]
|
||||
bar: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
}, ([operations, results], label) => {
|
||||
it(label, function () {
|
||||
forEach(operations, ([method, ...args]) => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import NotImplemented from './not-implemented'
|
||||
import {
|
||||
ACTION_ADD,
|
||||
ACTION_UPDATE,
|
||||
ACTION_REMOVE
|
||||
ACTION_REMOVE,
|
||||
} from './collection'
|
||||
|
||||
// ===================================================================
|
||||
@@ -77,7 +77,7 @@ export default class Index {
|
||||
const {
|
||||
computeHash,
|
||||
_itemsByHash: itemsByHash,
|
||||
_keysToHash: keysToHash
|
||||
_keysToHash: keysToHash,
|
||||
} = this
|
||||
|
||||
for (const key in items) {
|
||||
@@ -104,7 +104,7 @@ export default class Index {
|
||||
const {
|
||||
computeHash,
|
||||
_itemsByHash: itemsByHash,
|
||||
_keysToHash: keysToHash
|
||||
_keysToHash: keysToHash,
|
||||
} = this
|
||||
|
||||
for (const key in items) {
|
||||
@@ -135,7 +135,7 @@ export default class Index {
|
||||
_onRemove (items) {
|
||||
const {
|
||||
_itemsByHash: itemsByHash,
|
||||
_keysToHash: keysToHash
|
||||
_keysToHash: keysToHash,
|
||||
} = this
|
||||
|
||||
for (const key in items) {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
import eventToPromise from 'event-to-promise'
|
||||
import { forEach } from 'lodash'
|
||||
|
||||
import Collection from '..'
|
||||
import Index from '../index'
|
||||
import Collection from './collection'
|
||||
import Index from './index'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -29,18 +29,18 @@ describe('Index', function () {
|
||||
let col, byGroup
|
||||
const item1 = {
|
||||
id: '2ccb8a72-dc65-48e4-88fe-45ef541f2cba',
|
||||
group: 'foo'
|
||||
group: 'foo',
|
||||
}
|
||||
const item2 = {
|
||||
id: '7d21dc51-4da8-4538-a2e9-dd6f4784eb76',
|
||||
group: 'bar'
|
||||
group: 'bar',
|
||||
}
|
||||
const item3 = {
|
||||
id: '668c1274-4442-44a6-b99a-512188e0bb09',
|
||||
group: 'foo'
|
||||
group: 'foo',
|
||||
}
|
||||
const item4 = {
|
||||
id: 'd90b7335-e540-4a44-ad22-c4baae9cd0a9'
|
||||
id: 'd90b7335-e540-4a44-ad22-c4baae9cd0a9',
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
@@ -61,19 +61,19 @@ describe('Index', function () {
|
||||
byGroup: {
|
||||
foo: {
|
||||
[item1.id]: item1,
|
||||
[item3.id]: item3
|
||||
[item3.id]: item3,
|
||||
},
|
||||
bar: {
|
||||
[item2.id]: item2
|
||||
}
|
||||
}
|
||||
[item2.id]: item2,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('works with added items', function () {
|
||||
const item5 = {
|
||||
id: '823b56c4-4b96-4f3a-9533-5d08177167ac',
|
||||
group: 'baz'
|
||||
group: 'baz',
|
||||
}
|
||||
|
||||
col.add(item5)
|
||||
@@ -83,15 +83,15 @@ describe('Index', function () {
|
||||
byGroup: {
|
||||
foo: {
|
||||
[item1.id]: item1,
|
||||
[item3.id]: item3
|
||||
[item3.id]: item3,
|
||||
},
|
||||
bar: {
|
||||
[item2.id]: item2
|
||||
[item2.id]: item2,
|
||||
},
|
||||
baz: {
|
||||
[item5.id]: item5
|
||||
}
|
||||
}
|
||||
[item5.id]: item5,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -99,7 +99,7 @@ describe('Index', function () {
|
||||
it('works with updated items', function () {
|
||||
const item1bis = {
|
||||
id: item1.id,
|
||||
group: 'bar'
|
||||
group: 'bar',
|
||||
}
|
||||
|
||||
col.update(item1bis)
|
||||
@@ -108,13 +108,13 @@ describe('Index', function () {
|
||||
expect(col.indexes).toEqual({
|
||||
byGroup: {
|
||||
foo: {
|
||||
[item3.id]: item3
|
||||
[item3.id]: item3,
|
||||
},
|
||||
bar: {
|
||||
[item1.id]: item1bis,
|
||||
[item2.id]: item2
|
||||
}
|
||||
}
|
||||
[item2.id]: item2,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -127,10 +127,10 @@ describe('Index', function () {
|
||||
byGroup: {
|
||||
foo: {
|
||||
[item1.id]: item1,
|
||||
[item3.id]: item3
|
||||
[item3.id]: item3,
|
||||
},
|
||||
bar: {}
|
||||
}
|
||||
bar: {},
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -139,7 +139,7 @@ describe('Index', function () {
|
||||
const item1bis = {
|
||||
id: item1.id,
|
||||
group: item1.group,
|
||||
newProp: true
|
||||
newProp: true,
|
||||
}
|
||||
|
||||
col.update(item1bis)
|
||||
@@ -149,12 +149,12 @@ describe('Index', function () {
|
||||
byGroup: {
|
||||
foo: {
|
||||
[item1.id]: item1bis,
|
||||
[item3.id]: item3
|
||||
[item3.id]: item3,
|
||||
},
|
||||
bar: {
|
||||
[item2.id]: item2
|
||||
}
|
||||
}
|
||||
[item2.id]: item2,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -170,9 +170,9 @@ describe('Index', function () {
|
||||
byGroup: {
|
||||
foo: {
|
||||
[item1.id]: item1,
|
||||
[item3.id]: item3
|
||||
}
|
||||
}
|
||||
[item3.id]: item3,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@ import NotImplemented from './not-implemented'
|
||||
import {
|
||||
ACTION_ADD,
|
||||
ACTION_UPDATE,
|
||||
ACTION_REMOVE
|
||||
ACTION_REMOVE,
|
||||
} from './collection'
|
||||
|
||||
// ===================================================================
|
||||
@@ -66,7 +66,7 @@ export default class UniqueIndex {
|
||||
const {
|
||||
computeHash,
|
||||
_itemByHash: itemByHash,
|
||||
_keysToHash: keysToHash
|
||||
_keysToHash: keysToHash,
|
||||
} = this
|
||||
|
||||
for (const key in items) {
|
||||
@@ -85,7 +85,7 @@ export default class UniqueIndex {
|
||||
const {
|
||||
computeHash,
|
||||
_itemByHash: itemByHash,
|
||||
_keysToHash: keysToHash
|
||||
_keysToHash: keysToHash,
|
||||
} = this
|
||||
|
||||
for (const key in items) {
|
||||
@@ -110,7 +110,7 @@ export default class UniqueIndex {
|
||||
_onRemove (items) {
|
||||
const {
|
||||
_itemByHash: itemByHash,
|
||||
_keysToHash: keysToHash
|
||||
_keysToHash: keysToHash,
|
||||
} = this
|
||||
|
||||
for (const key in items) {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
import eventToPromise from 'event-to-promise'
|
||||
import { forEach } from 'lodash'
|
||||
|
||||
import Collection from '..'
|
||||
import Index from '../unique-index'
|
||||
import Collection from './collection'
|
||||
import Index from './unique-index'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -29,14 +29,14 @@ describe('UniqueIndex', function () {
|
||||
let col, byKey
|
||||
const item1 = {
|
||||
id: '2ccb8a72-dc65-48e4-88fe-45ef541f2cba',
|
||||
key: '036dee1b-9a3b-4fb5-be8a-4f535b355581'
|
||||
key: '036dee1b-9a3b-4fb5-be8a-4f535b355581',
|
||||
}
|
||||
const item2 = {
|
||||
id: '7d21dc51-4da8-4538-a2e9-dd6f4784eb76',
|
||||
key: '103cd893-d2cc-4d37-96fd-c259ad04c0d4'
|
||||
key: '103cd893-d2cc-4d37-96fd-c259ad04c0d4',
|
||||
}
|
||||
const item3 = {
|
||||
id: '668c1274-4442-44a6-b99a-512188e0bb09'
|
||||
id: '668c1274-4442-44a6-b99a-512188e0bb09',
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
@@ -56,15 +56,15 @@ describe('UniqueIndex', function () {
|
||||
expect(col.indexes).toEqual({
|
||||
byKey: {
|
||||
[item1.key]: item1,
|
||||
[item2.key]: item2
|
||||
}
|
||||
[item2.key]: item2,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('works with added items', function () {
|
||||
const item4 = {
|
||||
id: '823b56c4-4b96-4f3a-9533-5d08177167ac',
|
||||
key: '1437af14-429a-40db-8a51-8a2f5ed03201'
|
||||
key: '1437af14-429a-40db-8a51-8a2f5ed03201',
|
||||
}
|
||||
|
||||
col.add(item4)
|
||||
@@ -74,8 +74,8 @@ describe('UniqueIndex', function () {
|
||||
byKey: {
|
||||
[item1.key]: item1,
|
||||
[item2.key]: item2,
|
||||
[item4.key]: item4
|
||||
}
|
||||
[item4.key]: item4,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -83,7 +83,7 @@ describe('UniqueIndex', function () {
|
||||
it('works with updated items', function () {
|
||||
const item1bis = {
|
||||
id: item1.id,
|
||||
key: 'e03d4a3a-0331-4aca-97a2-016bbd43a29b'
|
||||
key: 'e03d4a3a-0331-4aca-97a2-016bbd43a29b',
|
||||
}
|
||||
|
||||
col.update(item1bis)
|
||||
@@ -92,8 +92,8 @@ describe('UniqueIndex', function () {
|
||||
expect(col.indexes).toEqual({
|
||||
byKey: {
|
||||
[item1bis.key]: item1bis,
|
||||
[item2.key]: item2
|
||||
}
|
||||
[item2.key]: item2,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -104,8 +104,8 @@ describe('UniqueIndex', function () {
|
||||
return waitTicks().then(() => {
|
||||
expect(col.indexes).toEqual({
|
||||
byKey: {
|
||||
[item1.key]: item1
|
||||
}
|
||||
[item1.key]: item1,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -114,7 +114,7 @@ describe('UniqueIndex', function () {
|
||||
const item1bis = {
|
||||
id: item1.id,
|
||||
key: item1.key,
|
||||
newProp: true
|
||||
newProp: true,
|
||||
}
|
||||
|
||||
col.update(item1bis)
|
||||
@@ -123,8 +123,8 @@ describe('UniqueIndex', function () {
|
||||
expect(col.indexes).toEqual({
|
||||
byKey: {
|
||||
[item1.key]: item1bis,
|
||||
[item2.key]: item2
|
||||
}
|
||||
[item2.key]: item2,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { forEach } from 'lodash'
|
||||
|
||||
import Collection from '..'
|
||||
import View from '../view'
|
||||
import Collection from './collection'
|
||||
import View from './view'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -11,18 +11,18 @@ users.getKey = (user) => user.name
|
||||
|
||||
// Inserts some data.
|
||||
users.add({
|
||||
name: 'bob'
|
||||
name: 'bob',
|
||||
})
|
||||
users.add({
|
||||
name: 'clara',
|
||||
active: true
|
||||
active: true,
|
||||
})
|
||||
users.add({
|
||||
name: 'ophelia'
|
||||
name: 'ophelia',
|
||||
})
|
||||
users.add({
|
||||
name: 'Steve',
|
||||
active: true
|
||||
active: true,
|
||||
})
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
@@ -48,9 +48,9 @@ setTimeout(function () {
|
||||
|
||||
users.set({
|
||||
name: 'ophelia',
|
||||
active: true
|
||||
active: true,
|
||||
})
|
||||
users.set({
|
||||
name: 'Steve'
|
||||
name: 'Steve',
|
||||
})
|
||||
}, 10)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { bind, forEach, iteratee as createCallback } from 'lodash'
|
||||
import Collection, {
|
||||
ACTION_ADD,
|
||||
ACTION_UPDATE,
|
||||
ACTION_REMOVE
|
||||
ACTION_REMOVE,
|
||||
} from './collection'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = require('./dist/unique-index')
|
||||
module.exports = require('./dist/unique-index') // eslint-disable-line node/no-missing-require
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = require('./dist/view')
|
||||
module.exports = require('./dist/view') // eslint-disable-line node/no-missing-require
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = require('./dist/api-errors')
|
||||
module.exports = require('./dist/api-errors') // eslint-disable-line node/no-missing-require
|
||||
|
||||
@@ -33,20 +33,17 @@
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"cross-env": "^5.0.1",
|
||||
"dependency-check": "^2.9.1",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"clean": "rimraf dist/",
|
||||
"depcheck": "dependency-check ./package.json --entry api-errors.js",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "yarn run depcheck",
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run clean",
|
||||
"prepublish": "yarn run build"
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
@@ -62,7 +59,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-0"
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class XoError extends BaseError {
|
||||
return {
|
||||
message: this.message,
|
||||
code: this.code,
|
||||
data: this.data
|
||||
data: this.data,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,44 +28,44 @@ const create = (code, getProps) => {
|
||||
// =============================================================================
|
||||
|
||||
export const notImplemented = create(0, () => ({
|
||||
message: 'not implemented'
|
||||
message: 'not implemented',
|
||||
}))
|
||||
|
||||
export const noSuchObject = create(1, (id, type) => ({
|
||||
data: { id, type },
|
||||
message: 'no such object'
|
||||
message: 'no such object',
|
||||
}))
|
||||
|
||||
export const unauthorized = create(2, () => ({
|
||||
message: 'not authenticated or not enough permissions'
|
||||
message: 'not authenticated or not enough permissions',
|
||||
}))
|
||||
|
||||
export const invalidCredentials = create(3, () => ({
|
||||
message: 'invalid credentials'
|
||||
message: 'invalid credentials',
|
||||
}))
|
||||
|
||||
// Deprecated alreadyAuthenticated (4)
|
||||
|
||||
export const forbiddenOperation = create(5, (operation, reason) => ({
|
||||
data: { operation, reason },
|
||||
message: `forbidden operation: ${operation}`
|
||||
message: `forbidden operation: ${operation}`,
|
||||
}))
|
||||
|
||||
// Deprecated GenericError (6)
|
||||
|
||||
export const noHostsAvailable = create(7, () => ({
|
||||
message: 'no hosts available'
|
||||
message: 'no hosts available',
|
||||
}))
|
||||
|
||||
export const authenticationFailed = create(8, () => ({
|
||||
message: 'authentication failed'
|
||||
message: 'authentication failed',
|
||||
}))
|
||||
|
||||
export const serverUnreachable = create(9, objectId => ({
|
||||
data: {
|
||||
objectId
|
||||
objectId,
|
||||
},
|
||||
message: 'server unreachable'
|
||||
message: 'server unreachable',
|
||||
}))
|
||||
|
||||
export const invalidParameters = create(10, (message, errors) => {
|
||||
@@ -76,22 +76,22 @@ export const invalidParameters = create(10, (message, errors) => {
|
||||
|
||||
return {
|
||||
data: { errors },
|
||||
message: message || 'invalid parameters'
|
||||
message: message || 'invalid parameters',
|
||||
}
|
||||
})
|
||||
|
||||
export const vmMissingPvDrivers = create(11, ({ vm }) => ({
|
||||
data: {
|
||||
objectId: vm
|
||||
objectId: vm,
|
||||
},
|
||||
message: 'missing PV drivers'
|
||||
message: 'missing PV drivers',
|
||||
}))
|
||||
|
||||
export const vmIsTemplate = create(12, ({ vm }) => ({
|
||||
data: {
|
||||
objectId: vm
|
||||
objectId: vm,
|
||||
},
|
||||
message: 'VM is a template'
|
||||
message: 'VM is a template',
|
||||
}))
|
||||
|
||||
// TODO: We should probably create a more generic error which gathers all incorrect state errors.
|
||||
@@ -109,58 +109,58 @@ export const vmBadPowerState = create(13, ({ vm, expected, actual }) => ({
|
||||
data: {
|
||||
objectId: vm,
|
||||
expected,
|
||||
actual
|
||||
actual,
|
||||
},
|
||||
message: `VM state is ${actual} but should be ${expected}`
|
||||
message: `VM state is ${actual} but should be ${expected}`,
|
||||
}))
|
||||
|
||||
export const vmLacksFeature = create(14, ({ vm, feature }) => ({
|
||||
data: {
|
||||
objectId: vm,
|
||||
feature
|
||||
feature,
|
||||
},
|
||||
message: `VM lacks feature ${feature || ''}`
|
||||
message: `VM lacks feature ${feature || ''}`,
|
||||
}))
|
||||
|
||||
export const notSupportedDuringUpgrade = create(15, () => ({
|
||||
message: 'not supported during upgrade'
|
||||
message: 'not supported during upgrade',
|
||||
}))
|
||||
|
||||
export const objectAlreadyExists = create(16, ({ objectId, objectType }) => ({
|
||||
data: {
|
||||
objectId,
|
||||
objectType
|
||||
objectType,
|
||||
},
|
||||
message: `${objectType || 'object'} already exists`
|
||||
message: `${objectType || 'object'} already exists`,
|
||||
}))
|
||||
|
||||
export const vdiInUse = create(17, ({ vdi, operation }) => ({
|
||||
data: {
|
||||
objectId: vdi,
|
||||
operation
|
||||
operation,
|
||||
},
|
||||
message: 'VDI in use'
|
||||
message: 'VDI in use',
|
||||
}))
|
||||
|
||||
export const hostOffline = create(18, ({ host }) => ({
|
||||
data: {
|
||||
objectId: host
|
||||
objectId: host,
|
||||
},
|
||||
message: 'host offline'
|
||||
message: 'host offline',
|
||||
}))
|
||||
|
||||
export const operationBlocked = create(19, ({ objectId, code }) => ({
|
||||
data: {
|
||||
objectId,
|
||||
code
|
||||
code,
|
||||
},
|
||||
message: 'operation blocked'
|
||||
message: 'operation blocked',
|
||||
}))
|
||||
|
||||
export const patchPrecheckFailed = create(20, ({ errorType, patch }) => ({
|
||||
data: {
|
||||
objectId: patch,
|
||||
errorType
|
||||
errorType,
|
||||
},
|
||||
message: `patch precheck failed: ${errorType}`
|
||||
message: `patch precheck failed: ${errorType}`,
|
||||
}))
|
||||
|
||||
@@ -4,10 +4,10 @@ process.on('unhandledRejection', function (error) {
|
||||
console.log(error)
|
||||
})
|
||||
|
||||
var Xo = require('./').default
|
||||
const Xo = require('./').default // eslint-disable-line node/no-missing-require
|
||||
|
||||
var xo = new Xo({
|
||||
url: 'localhost:9000'
|
||||
const xo = new Xo({
|
||||
url: 'localhost:9000',
|
||||
})
|
||||
|
||||
xo.open().then(function () {
|
||||
@@ -19,7 +19,7 @@ xo.open().then(function () {
|
||||
}).then(function () {
|
||||
return xo.signIn({
|
||||
email: 'admin@admin.net',
|
||||
password: 'admin'
|
||||
password: 'admin',
|
||||
}).then(function () {
|
||||
console.log('connected as ', xo.user)
|
||||
}).catch(function (error) {
|
||||
@@ -28,7 +28,7 @@ xo.open().then(function () {
|
||||
}).then(function () {
|
||||
return xo.signIn({
|
||||
email: 'tom',
|
||||
password: 'tom'
|
||||
password: 'tom',
|
||||
}).then(function () {
|
||||
console.log('connected as', xo.user)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"node": ">=4"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonrpc-websocket-client": "^0.2.0",
|
||||
"jsonrpc-websocket-client": "^0.3.0",
|
||||
"lodash": "^4.17.2",
|
||||
"make-error": "^1.0.4"
|
||||
},
|
||||
@@ -37,18 +37,15 @@
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.0.1",
|
||||
"dependency-check": "^2.9.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"commitmsg": "npm test",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "dependency-check ./package.json",
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "npm run prebuild",
|
||||
"prepublish": "npm run build"
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"babel": {
|
||||
"env": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import JsonRpcWebSocketClient, {
|
||||
OPEN,
|
||||
CLOSED
|
||||
CLOSED,
|
||||
} from 'jsonrpc-websocket-client'
|
||||
import { BaseError } from 'make-error'
|
||||
import { startsWith } from 'lodash'
|
||||
|
||||
@@ -31,18 +31,16 @@
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.0.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"deep-freeze": "^0.0.1",
|
||||
"dependency-check": "^2.9.1",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "dependency-check ./package.json",
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublish": "yarn run build"
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
|
||||
@@ -12,8 +12,8 @@ const data = deepFreeze({
|
||||
string: 'file:///var/lib/xoa/backup',
|
||||
object: {
|
||||
type: 'file',
|
||||
path: '/var/lib/xoa/backup'
|
||||
}
|
||||
path: '/var/lib/xoa/backup',
|
||||
},
|
||||
},
|
||||
SMB: {
|
||||
string: 'smb://Administrator:pas:sw@ord@toto\\\\192.168.100.225\\smb\0',
|
||||
@@ -23,17 +23,17 @@ const data = deepFreeze({
|
||||
path: '',
|
||||
domain: 'toto',
|
||||
username: 'Administrator',
|
||||
password: 'pas:sw@ord'
|
||||
}
|
||||
password: 'pas:sw@ord',
|
||||
},
|
||||
},
|
||||
NFS: {
|
||||
string: 'nfs://192.168.100.225:/media/nfs',
|
||||
object: {
|
||||
type: 'nfs',
|
||||
host: '192.168.100.225',
|
||||
path: '/media/nfs'
|
||||
}
|
||||
}
|
||||
path: '/media/nfs',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const parseData = deepFreeze({
|
||||
@@ -43,17 +43,17 @@ const parseData = deepFreeze({
|
||||
string: 'file://var/lib/xoa/backup',
|
||||
object: {
|
||||
type: 'file',
|
||||
path: '/var/lib/xoa/backup'
|
||||
}
|
||||
path: '/var/lib/xoa/backup',
|
||||
},
|
||||
},
|
||||
'nfs with missing leading slash': {
|
||||
string: 'nfs://192.168.100.225:media/nfs',
|
||||
object: {
|
||||
type: 'nfs',
|
||||
host: '192.168.100.225',
|
||||
path: '/media/nfs'
|
||||
}
|
||||
}
|
||||
path: '/media/nfs',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const formatData = deepFreeze({
|
||||
@@ -63,9 +63,9 @@ const formatData = deepFreeze({
|
||||
string: 'file:///var/lib/xoa/backup',
|
||||
object: {
|
||||
type: 'local',
|
||||
path: '/var/lib/xoa/backup'
|
||||
}
|
||||
}
|
||||
path: '/var/lib/xoa/backup',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
@@ -35,24 +35,26 @@
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-plugin-transform-runtime": "^6.15.0",
|
||||
"babel-preset-latest": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"dependency-check": "^2.9.1"
|
||||
"babel-preset-env": "^1.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"depcheck": "dependency-check ./package.json",
|
||||
"dev": "NODE_DEV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "yarn run depcheck",
|
||||
"prepublish": "yarn run build"
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"transform-runtime"
|
||||
],
|
||||
"presets": [
|
||||
"latest",
|
||||
"stage-0"
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ export const configurationSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
clientID: {
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
},
|
||||
clientSecret: {
|
||||
type: 'string'
|
||||
}
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['clientID', 'clientSecret']
|
||||
required: ['clientID', 'clientSecret'],
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- stable
|
||||
- 6
|
||||
- 4
|
||||
|
||||
# Use containers.
|
||||
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
||||
sudo: false
|
||||
@@ -41,18 +41,15 @@
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.3.3",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.1",
|
||||
"dependency-check": "^2.8.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"commitmsg": "npm test",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "dependency-check ./package.json",
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "npm run prebuild",
|
||||
"prepublish": "npm run build"
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"babel": {
|
||||
"env": {
|
||||
|
||||
@@ -7,22 +7,22 @@ export const configurationSchema = {
|
||||
properties: {
|
||||
callbackURL: {
|
||||
type: 'string',
|
||||
description: 'Must be exactly the same as specified on the Google developer console.'
|
||||
description: 'Must be exactly the same as specified on the Google developer console.',
|
||||
},
|
||||
clientID: {
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
},
|
||||
clientSecret: {
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
},
|
||||
scope: {
|
||||
default: 'https://www.googleapis.com/auth/plus.login',
|
||||
description: 'Note that changing this value will break existing users.',
|
||||
enum: [ 'https://www.googleapis.com/auth/plus.login', 'email' ],
|
||||
enumNames: [ 'Google+ name', 'Simple email address' ]
|
||||
}
|
||||
enumNames: [ 'Google+ name', 'Simple email address' ],
|
||||
},
|
||||
},
|
||||
required: ['callbackURL', 'clientID', 'clientSecret']
|
||||
required: ['callbackURL', 'clientID', 'clientSecret'],
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server-auth-ldap",
|
||||
"version": "0.6.3",
|
||||
"version": "0.6.4",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "LDAP authentication plugin for XO-Server",
|
||||
"keywords": [
|
||||
@@ -36,7 +36,7 @@
|
||||
"babel-runtime": "^6.22.0",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"exec-promise": "^0.7.0",
|
||||
"inquirer": "^3.0.1",
|
||||
"inquirer": "^5.0.0",
|
||||
"ldapjs": "^1.0.1",
|
||||
"lodash": "^4.17.4",
|
||||
"promise-toolbox": "^0.9.5"
|
||||
@@ -47,18 +47,15 @@
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.0.1",
|
||||
"dependency-check": "^2.9.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"commitmsg": "npm test",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "dependency-check ./package.json",
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "npm run prebuild",
|
||||
"prepublish": "npm run build"
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
|
||||
@@ -25,7 +25,7 @@ export const configurationSchema = {
|
||||
properties: {
|
||||
uri: {
|
||||
description: 'URI of the LDAP server.',
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
},
|
||||
certificateAuthorities: {
|
||||
description: `
|
||||
@@ -35,13 +35,13 @@ If not specified, it will use a default set of well-known CAs.
|
||||
`.trim(),
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string'
|
||||
}
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
checkCertificate: {
|
||||
description: 'Enforce the validity of the server\'s certificates. You can disable it when connecting to servers that use a self-signed certificate.',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
bind: {
|
||||
description: 'Credentials to use before looking for the user record.',
|
||||
@@ -55,18 +55,18 @@ Example: uid=xoa-auth,ou=people,dc=company,dc=net
|
||||
|
||||
For Microsoft Active Directory, it can also be \`<user>@<domain>\`.
|
||||
`.trim(),
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
},
|
||||
password: {
|
||||
description: 'Password of the user permitted of search the LDAP directory.',
|
||||
type: 'string'
|
||||
}
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['dn', 'password']
|
||||
required: ['dn', 'password'],
|
||||
},
|
||||
base: {
|
||||
description: 'The base is the part of the description tree where the users are looked for.',
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
},
|
||||
filter: {
|
||||
description: `
|
||||
@@ -85,10 +85,10 @@ something like:
|
||||
- \`(&(uid={{name}})(memberOf=<group DN>))\`
|
||||
`.trim(),
|
||||
type: 'string',
|
||||
default: '(uid={{name}})'
|
||||
}
|
||||
default: '(uid={{name}})',
|
||||
},
|
||||
},
|
||||
required: ['uri', 'base']
|
||||
required: ['uri', 'base'],
|
||||
}
|
||||
|
||||
export const testSchema = {
|
||||
@@ -96,14 +96,14 @@ export const testSchema = {
|
||||
properties: {
|
||||
username: {
|
||||
description: 'LDAP username',
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
},
|
||||
password: {
|
||||
description: 'LDAP password',
|
||||
type: 'string'
|
||||
}
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['username', 'password']
|
||||
required: ['username', 'password'],
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
@@ -119,14 +119,14 @@ class AuthLdap {
|
||||
const clientOpts = this._clientOpts = {
|
||||
url: conf.uri,
|
||||
maxConnections: 5,
|
||||
tlsOptions: {}
|
||||
tlsOptions: {},
|
||||
}
|
||||
|
||||
{
|
||||
const {
|
||||
bind,
|
||||
checkCertificate = true,
|
||||
certificateAuthorities
|
||||
certificateAuthorities,
|
||||
} = conf
|
||||
|
||||
if (bind) {
|
||||
@@ -147,7 +147,7 @@ class AuthLdap {
|
||||
const {
|
||||
bind: credentials,
|
||||
base: searchBase,
|
||||
filter: searchFilter = '(uid={{name}})'
|
||||
filter: searchFilter = '(uid={{name}})',
|
||||
} = conf
|
||||
|
||||
this._credentials = credentials
|
||||
@@ -166,7 +166,7 @@ class AuthLdap {
|
||||
test ({ username, password }) {
|
||||
return this._authenticate({
|
||||
username,
|
||||
password
|
||||
password,
|
||||
}).then(result => {
|
||||
if (result === null) {
|
||||
throw new Error('could not authenticate user')
|
||||
@@ -207,8 +207,8 @@ class AuthLdap {
|
||||
const response = await search(this._searchBase, {
|
||||
scope: 'sub',
|
||||
filter: evalFilter(this._searchFilter, {
|
||||
name: username
|
||||
})
|
||||
name: username,
|
||||
}),
|
||||
})
|
||||
|
||||
response.on('searchEntry', entry => {
|
||||
|
||||
@@ -9,46 +9,46 @@ const EMPTY_OBJECT = Object.freeze({ __proto__: null })
|
||||
const _extractValue = ({ value }) => value
|
||||
|
||||
export const confirm = (message, {
|
||||
default: defaultValue = null
|
||||
default: defaultValue = null,
|
||||
} = EMPTY_OBJECT) => prompt({
|
||||
default: defaultValue,
|
||||
message,
|
||||
name: 'value',
|
||||
type: 'confirm'
|
||||
type: 'confirm',
|
||||
}).then(_extractValue)
|
||||
|
||||
export const input = (message, {
|
||||
default: defaultValue = null,
|
||||
filter = undefined,
|
||||
validate = undefined
|
||||
validate = undefined,
|
||||
} = EMPTY_OBJECT) => prompt({
|
||||
default: defaultValue,
|
||||
message,
|
||||
name: 'value',
|
||||
type: 'input',
|
||||
validate
|
||||
validate,
|
||||
}).then(_extractValue)
|
||||
|
||||
export const list = (message, choices, {
|
||||
default: defaultValue = null
|
||||
default: defaultValue = null,
|
||||
} = EMPTY_OBJECT) => prompt({
|
||||
default: defaultValue,
|
||||
choices,
|
||||
message,
|
||||
name: 'value',
|
||||
type: 'list'
|
||||
type: 'list',
|
||||
}).then(_extractValue)
|
||||
|
||||
export const password = (message, {
|
||||
default: defaultValue = null,
|
||||
filter = undefined,
|
||||
validate = undefined
|
||||
validate = undefined,
|
||||
} = EMPTY_OBJECT) => prompt({
|
||||
default: defaultValue,
|
||||
message,
|
||||
name: 'value',
|
||||
type: 'password',
|
||||
validate
|
||||
validate,
|
||||
}).then(_extractValue)
|
||||
|
||||
// ===================================================================
|
||||
@@ -86,7 +86,7 @@ const promptByType = {
|
||||
while (
|
||||
i < n && // eslint-disable-line no-unmodified-loop-condition
|
||||
await confirm('additional item?', {
|
||||
default: false
|
||||
default: false,
|
||||
})
|
||||
) {
|
||||
await promptItem()
|
||||
@@ -96,23 +96,23 @@ const promptByType = {
|
||||
},
|
||||
|
||||
boolean: (schema, defaultValue, path) => confirm(path, {
|
||||
default: defaultValue != null ? defaultValue : schema.default
|
||||
default: defaultValue != null ? defaultValue : schema.default,
|
||||
}),
|
||||
|
||||
enum: (schema, defaultValue, path) => list(path, schema.enum, {
|
||||
defaultValue: defaultValue || schema.defaultValue
|
||||
defaultValue: defaultValue || schema.defaultValue,
|
||||
}),
|
||||
|
||||
integer: (schema, defaultValue, path) => input(path, {
|
||||
default: defaultValue || schema.default,
|
||||
filter: input => +input,
|
||||
validate: input => isInteger(+input)
|
||||
validate: input => isInteger(+input),
|
||||
}),
|
||||
|
||||
number: (schema, defaultValue, path) => input(path, {
|
||||
default: defaultValue || schema.default,
|
||||
filter: input => +input,
|
||||
validate: input => isFinite(+input)
|
||||
validate: input => isFinite(+input),
|
||||
}),
|
||||
|
||||
object: async (schema, defaultValue, path) => {
|
||||
@@ -131,7 +131,7 @@ const promptByType = {
|
||||
if (
|
||||
required[name] ||
|
||||
await confirm(`fill optional ${subpath}?`, {
|
||||
default: Boolean(defaultValue && name in defaultValue)
|
||||
default: Boolean(defaultValue && name in defaultValue),
|
||||
})
|
||||
) {
|
||||
value[name] = await promptGeneric(
|
||||
@@ -148,8 +148,8 @@ const promptByType = {
|
||||
},
|
||||
|
||||
string: (schema, defaultValue, path) => input(path, {
|
||||
default: defaultValue || schema.default
|
||||
})
|
||||
default: defaultValue || schema.default,
|
||||
}),
|
||||
}
|
||||
|
||||
export default function promptGeneric (schema, defaultValue, path) {
|
||||
|
||||
@@ -7,10 +7,10 @@ import { readFile, writeFile } from 'fs'
|
||||
|
||||
import promptSchema, {
|
||||
input,
|
||||
password
|
||||
password,
|
||||
} from './prompt-schema'
|
||||
import createPlugin, {
|
||||
configurationSchema
|
||||
configurationSchema,
|
||||
} from './'
|
||||
|
||||
// ===================================================================
|
||||
@@ -42,8 +42,8 @@ execPromise(async args => {
|
||||
|
||||
await plugin._authenticate({
|
||||
username: await input('Username', {
|
||||
validate: input => !!input.length
|
||||
validate: input => !!input.length,
|
||||
}),
|
||||
password: await password('Password')
|
||||
password: await password('Password'),
|
||||
}, bind(console.log, console))
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server-auth-saml",
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "SAML authentication plugin for XO-Server",
|
||||
"keywords": [
|
||||
@@ -33,34 +33,38 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.11.6",
|
||||
"passport-saml": "^0.15.0"
|
||||
"passport-saml": "^0.32.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-latest": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"cross-env": "^5.0.1",
|
||||
"dependency-check": "^2.9.1",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"clean": "rimraf dist/",
|
||||
"depcheck": "dependency-check ./package.json",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "yarn run depcheck",
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run clean",
|
||||
"prepublish": "yarn run build"
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"transform-runtime"
|
||||
],
|
||||
"presets": [
|
||||
"latest",
|
||||
"stage-0"
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,19 @@ export const configurationSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
cert: {
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
},
|
||||
entryPoint: {
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
},
|
||||
issuer: {
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
},
|
||||
usernameField: {
|
||||
type: 'string'
|
||||
}
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['cert', 'entryPoint', 'issuer']
|
||||
required: ['cert', 'entryPoint', 'issuer'],
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -35,28 +35,24 @@
|
||||
"node": ">=4"
|
||||
},
|
||||
"dependencies": {
|
||||
"human-format": "^0.9.0",
|
||||
"human-format": "^0.9.2",
|
||||
"lodash": "^4.13.1",
|
||||
"moment-timezone": "^0.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-eslint": "^8.0.1",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"cross-env": "^5.0.1",
|
||||
"dependency-check": "^2.9.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"clean": "rimraf dist/",
|
||||
"depcheck": "dependency-check ./package.json",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "yarn run depcheck",
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run clean",
|
||||
"prepublish": "yarn run build"
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
|
||||
@@ -14,9 +14,9 @@ export const configurationSchema = {
|
||||
description: 'an array of recipients (mails)',
|
||||
|
||||
items: {
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
},
|
||||
minItems: 1
|
||||
minItems: 1,
|
||||
},
|
||||
toXmpp: {
|
||||
type: 'array',
|
||||
@@ -24,11 +24,11 @@ export const configurationSchema = {
|
||||
description: 'an array of recipients (xmpp)',
|
||||
|
||||
items: {
|
||||
type: 'string'
|
||||
type: 'string',
|
||||
},
|
||||
minItems: 1
|
||||
}
|
||||
}
|
||||
minItems: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
@@ -50,13 +50,13 @@ const formatMethod = method =>
|
||||
const formatSize = bytes =>
|
||||
humanFormat(bytes, {
|
||||
scale: 'binary',
|
||||
unit: 'B'
|
||||
unit: 'B',
|
||||
})
|
||||
|
||||
const formatSpeed = (bytes, milliseconds) =>
|
||||
humanFormat(bytes * 1e3 / milliseconds, {
|
||||
scale: 'binary',
|
||||
unit: 'B/s'
|
||||
unit: 'B/s',
|
||||
})
|
||||
|
||||
const logError = e => {
|
||||
@@ -115,7 +115,7 @@ class BackupReportsXoPlugin {
|
||||
|
||||
const reportOnFailure =
|
||||
reportWhen === 'fail' || // xo-web < 5
|
||||
reportWhen === 'failure' // xo-web >= 5
|
||||
reportWhen === 'failure' // xo-web >= 5
|
||||
|
||||
let globalMergeSize = 0
|
||||
let globalTransferSize = 0
|
||||
@@ -143,7 +143,7 @@ class BackupReportsXoPlugin {
|
||||
`- **UUID**: ${vm !== undefined ? vm.uuid : id}`,
|
||||
`- **Start time**: ${formatDate(start)}`,
|
||||
`- **End time**: ${formatDate(end)}`,
|
||||
`- **Duration**: ${formatDuration(duration)}`
|
||||
`- **Duration**: ${formatDuration(duration)}`,
|
||||
]
|
||||
|
||||
const { error } = call
|
||||
@@ -205,7 +205,7 @@ class BackupReportsXoPlugin {
|
||||
`- **Start time**: ${formatDate(start)}`,
|
||||
`- **End time**: ${formatDate(end)}`,
|
||||
`- **Duration**: ${formatDuration(duration)}`,
|
||||
`- **Successes**: ${nSuccesses} / ${nCalls}`
|
||||
`- **Successes**: ${nSuccesses} / ${nCalls}`,
|
||||
]
|
||||
if (globalTransferSize !== 0) {
|
||||
markdown.push(
|
||||
@@ -256,21 +256,21 @@ class BackupReportsXoPlugin {
|
||||
} − Backup report for ${tag} ${
|
||||
globalSuccess ? ICON_SUCCESS : ICON_FAILURE
|
||||
}`,
|
||||
markdown
|
||||
markdown,
|
||||
}),
|
||||
xo.sendToXmppClient !== undefined && xo.sendToXmppClient({
|
||||
to: this._xmppReceivers,
|
||||
message: markdown
|
||||
message: markdown,
|
||||
}),
|
||||
xo.sendSlackMessage !== undefined && xo.sendSlackMessage({
|
||||
message: markdown
|
||||
message: markdown,
|
||||
}),
|
||||
xo.sendPassiveCheck !== undefined && xo.sendPassiveCheck({
|
||||
status: globalSuccess ? 0 : 2,
|
||||
message: globalSuccess
|
||||
? `[Xen Orchestra] [Success] Backup report for ${tag}`
|
||||
: `[Xen Orchestra] [Failure] Backup report for ${tag} - VMs : ${nagiosText.join(' ')}`
|
||||
})
|
||||
: `[Xen Orchestra] [Failure] Backup report for ${tag} - VMs : ${nagiosText.join(' ')}`,
|
||||
}),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,27 +33,24 @@
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.20.0",
|
||||
"event-to-promise": "^0.8.0",
|
||||
"jsonrpc-websocket-client": "^0.2.0",
|
||||
"superagent": "^3.4.4"
|
||||
"jsonrpc-websocket-client": "^0.3.0",
|
||||
"superagent": "^3.8.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.18.0",
|
||||
"babel-plugin-transform-runtime": "^6.15.0",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.16.0",
|
||||
"cross-env": "^5.0.1",
|
||||
"dependency-check": "^2.9.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.5.4"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"clean": "rimraf dist/",
|
||||
"depcheck": "dependency-check ./package.json",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "yarn run depcheck",
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run clean",
|
||||
"prepublish": "yarn run build"
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
|
||||
@@ -13,7 +13,7 @@ class XoServerCloud {
|
||||
constructor ({ xo }) {
|
||||
this._xo = xo
|
||||
|
||||
// Defined in configure().
|
||||
// Defined in configure().
|
||||
this._conf = null
|
||||
this._key = null
|
||||
}
|
||||
@@ -31,16 +31,16 @@ class XoServerCloud {
|
||||
registerResource.description = 'Register a resource via cloud plugin'
|
||||
registerResource.params = {
|
||||
namespace: {
|
||||
type: 'string'
|
||||
}
|
||||
type: 'string',
|
||||
},
|
||||
}
|
||||
registerResource.permission = 'admin'
|
||||
|
||||
this._unsetApiMethods = this._xo.addApiMethods({
|
||||
cloud: {
|
||||
getResourceCatalog,
|
||||
registerResource
|
||||
}
|
||||
registerResource,
|
||||
},
|
||||
})
|
||||
this._unsetRequestResource = this._xo.defineProperty('requestResource', this._requestResource, this)
|
||||
|
||||
@@ -127,7 +127,7 @@ class XoServerCloud {
|
||||
const downloadToken = await this._updater.call('getResourceDownloadToken', {
|
||||
token: namespaceCatalog._token,
|
||||
id,
|
||||
version
|
||||
version,
|
||||
})
|
||||
|
||||
if (!downloadToken) {
|
||||
|
||||
@@ -39,16 +39,12 @@
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"dependency-check": "^2.9.1"
|
||||
"babel-preset-stage-3": "^6.24.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"depcheck": "dependency-check ./package.json",
|
||||
"dev": "NODE_DEV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "yarn run depcheck",
|
||||
"prepublish": "yarn run build"
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
@@ -56,8 +52,15 @@
|
||||
"lodash"
|
||||
],
|
||||
"presets": [
|
||||
"es2015",
|
||||
"stage-0"
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,11 @@ export default class DensityPlan extends Plan {
|
||||
|
||||
const {
|
||||
hosts,
|
||||
toOptimize
|
||||
toOptimize,
|
||||
} = results
|
||||
|
||||
let {
|
||||
averages: hostsAverages
|
||||
averages: hostsAverages,
|
||||
} = results
|
||||
|
||||
const pools = await this._getPlanPools()
|
||||
@@ -34,11 +34,11 @@ export default class DensityPlan extends Plan {
|
||||
for (const hostToOptimize of toOptimize) {
|
||||
const {
|
||||
id: hostId,
|
||||
$poolId: poolId
|
||||
$poolId: poolId,
|
||||
} = hostToOptimize
|
||||
|
||||
const {
|
||||
master: masterId
|
||||
master: masterId,
|
||||
} = pools[poolId]
|
||||
|
||||
// Avoid master optimization.
|
||||
@@ -60,7 +60,7 @@ export default class DensityPlan extends Plan {
|
||||
for (const dest of hosts) {
|
||||
const {
|
||||
id: destId,
|
||||
$poolId: destPoolId
|
||||
$poolId: destPoolId,
|
||||
} = dest
|
||||
|
||||
// Destination host != Host to optimize!
|
||||
@@ -87,9 +87,9 @@ export default class DensityPlan extends Plan {
|
||||
[ poolMaster ],
|
||||
poolHosts,
|
||||
masters,
|
||||
otherHosts
|
||||
otherHosts,
|
||||
],
|
||||
hostsAverages: clone(hostsAverages)
|
||||
hostsAverages: clone(hostsAverages),
|
||||
})
|
||||
|
||||
if (simulResults) {
|
||||
@@ -127,7 +127,7 @@ export default class DensityPlan extends Plan {
|
||||
|
||||
const simulResults = {
|
||||
hostsAverages,
|
||||
moves: []
|
||||
moves: [],
|
||||
}
|
||||
|
||||
// Try to find a destination for each VM.
|
||||
@@ -140,7 +140,7 @@ export default class DensityPlan extends Plan {
|
||||
vm,
|
||||
destinations: subDestinations,
|
||||
hostsAverages,
|
||||
vmsAverages
|
||||
vmsAverages,
|
||||
})
|
||||
|
||||
// Destination found.
|
||||
@@ -164,8 +164,8 @@ export default class DensityPlan extends Plan {
|
||||
_testMigration ({ vm, destinations, hostsAverages, vmsAverages }) {
|
||||
const {
|
||||
_thresholds: {
|
||||
critical: criticalThreshold
|
||||
}
|
||||
critical: criticalThreshold,
|
||||
},
|
||||
} = this
|
||||
|
||||
// Sort the destinations by available memory. (- -> +)
|
||||
@@ -192,7 +192,7 @@ export default class DensityPlan extends Plan {
|
||||
// Available movement.
|
||||
return {
|
||||
vm,
|
||||
destination
|
||||
destination,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,7 +206,7 @@ export default class DensityPlan extends Plan {
|
||||
mapToArray(moves, move => {
|
||||
const {
|
||||
vm,
|
||||
destination
|
||||
destination,
|
||||
} = move
|
||||
const xapiDest = this.xo.getXapi(destination)
|
||||
debug(`Migrate VM (${vm.id}) to Host (${destination.id}) from Host (${vm.$container}).`)
|
||||
|
||||
@@ -7,11 +7,11 @@ import DensityPlan from './density-plan'
|
||||
import PerformancePlan from './performance-plan'
|
||||
import {
|
||||
DEFAULT_CRITICAL_THRESHOLD_CPU,
|
||||
DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE
|
||||
DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE,
|
||||
} from './plan'
|
||||
import {
|
||||
EXECUTION_DELAY,
|
||||
debug
|
||||
debug,
|
||||
} from './utils'
|
||||
|
||||
// ===================================================================
|
||||
@@ -37,12 +37,12 @@ export const configurationSchema = {
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
title: 'Name'
|
||||
title: 'Name',
|
||||
},
|
||||
|
||||
mode: {
|
||||
enum: [ 'Performance mode', 'Density mode' ],
|
||||
title: 'Mode'
|
||||
title: 'Mode',
|
||||
},
|
||||
|
||||
pools: {
|
||||
@@ -51,8 +51,8 @@ export const configurationSchema = {
|
||||
|
||||
items: {
|
||||
type: 'string',
|
||||
$type: 'Pool'
|
||||
}
|
||||
$type: 'Pool',
|
||||
},
|
||||
},
|
||||
|
||||
thresholds: {
|
||||
@@ -63,14 +63,14 @@ export const configurationSchema = {
|
||||
cpu: {
|
||||
type: 'integer',
|
||||
title: 'CPU (%)',
|
||||
default: DEFAULT_CRITICAL_THRESHOLD_CPU
|
||||
default: DEFAULT_CRITICAL_THRESHOLD_CPU,
|
||||
},
|
||||
memoryFree: {
|
||||
type: 'integer',
|
||||
title: 'RAM, Free memory (MB)',
|
||||
default: DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE
|
||||
}
|
||||
}
|
||||
default: DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
excludedHosts: {
|
||||
@@ -80,19 +80,19 @@ export const configurationSchema = {
|
||||
|
||||
items: {
|
||||
type: 'string',
|
||||
$type: 'Host'
|
||||
}
|
||||
}
|
||||
$type: 'Host',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
required: [ 'name', 'mode', 'pools' ]
|
||||
required: [ 'name', 'mode', 'pools' ],
|
||||
},
|
||||
|
||||
minItems: 1
|
||||
}
|
||||
minItems: 1,
|
||||
},
|
||||
},
|
||||
|
||||
additionalProperties: false
|
||||
additionalProperties: false,
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
@@ -102,7 +102,7 @@ export const configurationSchema = {
|
||||
const makeJob = (cronPattern, fn) => {
|
||||
const job = {
|
||||
running: false,
|
||||
emitter: new EventEmitter()
|
||||
emitter: new EventEmitter(),
|
||||
}
|
||||
|
||||
job.cron = new CronJob(cronPattern, async () => {
|
||||
@@ -133,7 +133,7 @@ const makeJob = (cronPattern, fn) => {
|
||||
class LoadBalancerPlugin {
|
||||
constructor (xo) {
|
||||
this.xo = xo
|
||||
this._job = makeJob(`*/${EXECUTION_DELAY} * * * *`, ::this._executePlans)
|
||||
this._job = makeJob(`*/${EXECUTION_DELAY} * * * *`, this._executePlans.bind(this))
|
||||
}
|
||||
|
||||
async configure ({ plans }) {
|
||||
|
||||
@@ -54,7 +54,7 @@ export default class PerformancePlan extends Plan {
|
||||
|
||||
const {
|
||||
averages,
|
||||
toOptimize
|
||||
toOptimize,
|
||||
} = results
|
||||
let { hosts } = results
|
||||
|
||||
@@ -75,7 +75,7 @@ export default class PerformancePlan extends Plan {
|
||||
await this._optimize({
|
||||
exceededHost,
|
||||
hosts,
|
||||
hostsAverages: averages
|
||||
hostsAverages: averages,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { filter, includes, map as mapToArray } from 'lodash'
|
||||
|
||||
import {
|
||||
EXECUTION_DELAY,
|
||||
debug
|
||||
debug,
|
||||
} from './utils'
|
||||
|
||||
const MINUTES_OF_HISTORICAL_DATA = 30
|
||||
@@ -58,7 +58,7 @@ function computeRessourcesAverage (objects, objectsStats, nPoints) {
|
||||
),
|
||||
nCpus: stats.cpus.length,
|
||||
memoryFree: computeAverage(stats.memoryFree, nPoints),
|
||||
memory: computeAverage(stats.memory, nPoints)
|
||||
memory: computeAverage(stats.memory, nPoints),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ function setRealCpuAverageOfVms (vms, vmsAverages, nCpus) {
|
||||
export default class Plan {
|
||||
constructor (xo, name, poolIds, {
|
||||
excludedHosts,
|
||||
thresholds
|
||||
thresholds,
|
||||
} = {}) {
|
||||
this.xo = xo
|
||||
this._name = name
|
||||
@@ -99,11 +99,11 @@ export default class Plan {
|
||||
this._excludedHosts = excludedHosts
|
||||
this._thresholds = {
|
||||
cpu: {
|
||||
critical: numberOrDefault(thresholds && thresholds.cpu, DEFAULT_CRITICAL_THRESHOLD_CPU)
|
||||
critical: numberOrDefault(thresholds && thresholds.cpu, DEFAULT_CRITICAL_THRESHOLD_CPU),
|
||||
},
|
||||
memoryFree: {
|
||||
critical: numberOrDefault(thresholds && thresholds.memoryFree, DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE) * 1024
|
||||
}
|
||||
critical: numberOrDefault(thresholds && thresholds.memoryFree, DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE) * 1024,
|
||||
},
|
||||
}
|
||||
|
||||
for (const key in this._thresholds) {
|
||||
@@ -157,7 +157,7 @@ export default class Plan {
|
||||
return {
|
||||
toOptimize,
|
||||
averages: avgWithRatio,
|
||||
hosts
|
||||
hosts,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ export default class Plan {
|
||||
hostsStats[host.id] = {
|
||||
nPoints: hostStats.stats.cpus[0].length,
|
||||
stats: hostStats.stats,
|
||||
averages: {}
|
||||
averages: {},
|
||||
}
|
||||
})
|
||||
))
|
||||
@@ -229,7 +229,7 @@ export default class Plan {
|
||||
vmsStats[vm.id] = {
|
||||
nPoints: vmStats.stats.cpus[0].length,
|
||||
stats: vmStats.stats,
|
||||
averages: {}
|
||||
averages: {},
|
||||
}
|
||||
})
|
||||
))
|
||||
|
||||
@@ -9,10 +9,10 @@ exports.configurationSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
foo: {
|
||||
type: 'string'
|
||||
}
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['foo']
|
||||
required: ['foo'],
|
||||
}
|
||||
|
||||
// This (optional) schema is used to test the configuration
|
||||
@@ -21,10 +21,10 @@ exports.testSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
test: {
|
||||
type: 'string'
|
||||
}
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['test']
|
||||
required: ['test'],
|
||||
}
|
||||
|
||||
// The default export is just a factory function which will create an
|
||||
@@ -73,6 +73,6 @@ exports.default = function (opts) {
|
||||
test: function (data) {
|
||||
console.log('the configuration is about to be tested')
|
||||
// TODO: test the configuration, i.e, use the main feature of the plugin and throws any errors.
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xo-server-transport-email",
|
||||
"version": "0.4.2",
|
||||
"version": "0.5.0",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "xo-server plugin to send emails",
|
||||
"keywords": [
|
||||
@@ -31,7 +31,7 @@
|
||||
"node": ">=4"
|
||||
},
|
||||
"dependencies": {
|
||||
"nodemailer": "3.1.8",
|
||||
"nodemailer": "4.4.1",
|
||||
"nodemailer-markdown": "^1.0.1",
|
||||
"promise-toolbox": "^0.9.5"
|
||||
},
|
||||
@@ -40,17 +40,15 @@
|
||||
"babel-plugin-lodash": "^3.3.2",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.0.1",
|
||||
"dependency-check": "^2.9.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "dependency-check ./package.json",
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublish": "yarn run build"
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
|
||||
@@ -33,16 +33,16 @@ export const configurationSchema = {
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'human readable name of the sender'
|
||||
description: 'human readable name of the sender',
|
||||
},
|
||||
address: {
|
||||
type: 'string',
|
||||
description: 'email address of the sender'
|
||||
}
|
||||
description: 'email address of the sender',
|
||||
},
|
||||
},
|
||||
|
||||
additionalProperties: false,
|
||||
required: ['address']
|
||||
required: ['address'],
|
||||
},
|
||||
|
||||
transport: {
|
||||
@@ -51,11 +51,11 @@ export const configurationSchema = {
|
||||
properties: {
|
||||
host: {
|
||||
type: 'string',
|
||||
description: 'hostname or IP address of the SMTP server'
|
||||
description: 'hostname or IP address of the SMTP server',
|
||||
},
|
||||
port: {
|
||||
type: 'integer',
|
||||
description: 'port of the SMTP server (defaults to 25 or 465 for TLS)'
|
||||
description: 'port of the SMTP server (defaults to 25 or 465 for TLS)',
|
||||
},
|
||||
secure: {
|
||||
default: false,
|
||||
@@ -64,24 +64,24 @@ export const configurationSchema = {
|
||||
'auto (uses STARTTLS if available)',
|
||||
'force (requires STARTTLS or fail)',
|
||||
'disabled (never use STARTTLS)',
|
||||
'TLS'
|
||||
'TLS',
|
||||
],
|
||||
description: 'whether the connection should use TLS'
|
||||
description: 'whether the connection should use TLS',
|
||||
},
|
||||
ignoreUnauthorized: {
|
||||
type: 'boolean',
|
||||
description: 'ignore certificates error (e.g. self-signed certificate)'
|
||||
description: 'ignore certificates error (e.g. self-signed certificate)',
|
||||
},
|
||||
|
||||
// FIXME: xo-web does not support edition of too nested
|
||||
user: {
|
||||
type: 'string',
|
||||
description: 'name to use to authenticate'
|
||||
description: 'name to use to authenticate',
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
description: 'password to use to authenticate'
|
||||
}
|
||||
description: 'password to use to authenticate',
|
||||
},
|
||||
// properties.
|
||||
// auth: {
|
||||
// type: 'object',
|
||||
@@ -103,12 +103,12 @@ export const configurationSchema = {
|
||||
},
|
||||
|
||||
additionalProperties: false,
|
||||
required: ['host']
|
||||
}
|
||||
required: ['host'],
|
||||
},
|
||||
},
|
||||
|
||||
additionalProperties: false,
|
||||
required: ['from', 'transport']
|
||||
required: ['from', 'transport'],
|
||||
}
|
||||
|
||||
export const testSchema = {
|
||||
@@ -117,12 +117,12 @@ export const testSchema = {
|
||||
properties: {
|
||||
to: {
|
||||
type: 'string',
|
||||
description: 'recipient of the test mail'
|
||||
}
|
||||
description: 'recipient of the test mail',
|
||||
},
|
||||
},
|
||||
|
||||
additionalProperties: false,
|
||||
required: ['to']
|
||||
required: ['to'],
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
@@ -144,7 +144,7 @@ class TransportEmailPlugin {
|
||||
secure,
|
||||
user,
|
||||
...transportConf
|
||||
}
|
||||
},
|
||||
}) {
|
||||
if (ignoreUnauthorized != null) {
|
||||
(
|
||||
@@ -190,8 +190,8 @@ The transport-email plugin for Xen Orchestra server seems to be working fine, ni
|
||||
`,
|
||||
attachments: [ {
|
||||
filename: 'example.txt',
|
||||
content: 'Attachments are working too, great!\n'
|
||||
} ]
|
||||
content: 'Attachments are working too, great!\n',
|
||||
} ],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ The transport-email plugin for Xen Orchestra server seems to be working fine, ni
|
||||
to, cc, bcc,
|
||||
subject,
|
||||
markdown,
|
||||
attachments
|
||||
attachments,
|
||||
}) {
|
||||
return this._send(removeUndefined({
|
||||
from,
|
||||
@@ -209,7 +209,7 @@ The transport-email plugin for Xen Orchestra server seems to be working fine, ni
|
||||
bcc,
|
||||
subject,
|
||||
markdown,
|
||||
attachments
|
||||
attachments,
|
||||
})).catch(logAndRethrow)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,19 +39,16 @@
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.0.1",
|
||||
"dependency-check": "^2.9.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"clean": "rimraf dist/",
|
||||
"depcheck": "dependency-check ./package.json",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "yarn run depcheck",
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run clean",
|
||||
"prepublish": "yarn run build"
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
|
||||
@@ -10,27 +10,27 @@ export const configurationSchema = {
|
||||
properties: {
|
||||
server: {
|
||||
type: 'string',
|
||||
description: 'The nagios server adress'
|
||||
description: 'The nagios server adress',
|
||||
},
|
||||
port: {
|
||||
type: 'integer',
|
||||
description: 'The NSCA port'
|
||||
description: 'The NSCA port',
|
||||
},
|
||||
key: {
|
||||
type: 'string',
|
||||
description: 'The encryption key'
|
||||
description: 'The encryption key',
|
||||
},
|
||||
host: {
|
||||
type: 'string',
|
||||
description: 'The host name in Nagios'
|
||||
description: 'The host name in Nagios',
|
||||
},
|
||||
service: {
|
||||
type: 'string',
|
||||
description: 'The service description in Nagios'
|
||||
}
|
||||
description: 'The service description in Nagios',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
required: ['server', 'port', 'key', 'host', 'service']
|
||||
required: ['server', 'port', 'key', 'host', 'service'],
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
@@ -45,7 +45,7 @@ function nscaPacketBuilder ({
|
||||
message,
|
||||
service,
|
||||
status,
|
||||
timestamp
|
||||
timestamp,
|
||||
}) {
|
||||
// Building NSCA packet
|
||||
const SIZE = 720
|
||||
@@ -92,7 +92,7 @@ class XoServerNagios {
|
||||
this._set = bind(xo.defineProperty, xo)
|
||||
this._unset = null
|
||||
|
||||
// Defined in configure().
|
||||
// Defined in configure().
|
||||
this._conf = null
|
||||
this._key = null
|
||||
}
|
||||
@@ -113,13 +113,13 @@ class XoServerNagios {
|
||||
test () {
|
||||
return this._sendPassiveCheck({
|
||||
message: 'The server-nagios plugin for Xen Orchestra server seems to be working fine, nicely done :)',
|
||||
status: OK
|
||||
status: OK,
|
||||
})
|
||||
}
|
||||
|
||||
_sendPassiveCheck ({
|
||||
message,
|
||||
status
|
||||
status,
|
||||
}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (/\r|\n/.test(message)) {
|
||||
@@ -140,7 +140,7 @@ class XoServerNagios {
|
||||
iv,
|
||||
message,
|
||||
status,
|
||||
timestamp
|
||||
timestamp,
|
||||
})
|
||||
|
||||
// 1) Using xor between the NSCA packet and the initialization vector
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- stable
|
||||
- 6
|
||||
- 4
|
||||
|
||||
# Use containers.
|
||||
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
||||
sudo: false
|
||||
@@ -38,20 +38,17 @@
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.18.0",
|
||||
"babel-preset-env": "^1.0.0",
|
||||
"babel-preset-stage-0": "^6.16.0",
|
||||
"cross-env": "^5.1.1",
|
||||
"dependency-check": "^2.6.0",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.5.4"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"clean": "rimraf dist/",
|
||||
"depcheck": "dependency-check ./package.json",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "npm run depcheck",
|
||||
"prebuild": "npm run clean",
|
||||
"predev": "npm run clean",
|
||||
"prepublish": "npm run build"
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
@@ -63,7 +60,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-0"
|
||||
"stage-3"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
|
||||
@@ -16,34 +16,34 @@ export const configurationSchema = {
|
||||
properties: {
|
||||
webhookUri: {
|
||||
type: 'string',
|
||||
description: 'The Mattermost or Slack webhook URL.'
|
||||
description: 'The Mattermost or Slack webhook URL.',
|
||||
},
|
||||
channel: {
|
||||
type: 'string',
|
||||
description: 'Channel, private group, or IM channel to send message to.'
|
||||
description: 'Channel, private group, or IM channel to send message to.',
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
description: 'Bot username.'
|
||||
description: 'Bot username.',
|
||||
},
|
||||
icon_emoji: {
|
||||
type: 'string',
|
||||
description: 'The bot icon. It can be a slack emoji or a URL image.'
|
||||
}
|
||||
description: 'The bot icon. It can be a slack emoji or a URL image.',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
required: ['webhookUri', 'channel']
|
||||
required: ['webhookUri', 'channel'],
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
class XoServerTransportSlack {
|
||||
constructor ({ xo }) {
|
||||
this._sendSlack = ::this._sendSlack
|
||||
this._set = ::xo.defineProperty
|
||||
this._sendSlack = this._sendSlack.bind(this)
|
||||
this._set = xo.defineProperty.bind(xo)
|
||||
this._unset = null
|
||||
|
||||
// Defined in configure().
|
||||
// Defined in configure().
|
||||
this._conf = null
|
||||
this._send = null
|
||||
}
|
||||
@@ -70,12 +70,12 @@ class XoServerTransportSlack {
|
||||
return this._sendSlack({
|
||||
message: `Hi there,
|
||||
|
||||
The transport-slack plugin for Xen Orchestra server seems to be working fine, nicely done :)`
|
||||
The transport-slack plugin for Xen Orchestra server seems to be working fine, nicely done :)`,
|
||||
})
|
||||
}
|
||||
|
||||
_sendSlack ({
|
||||
message
|
||||
message,
|
||||
}) {
|
||||
// TODO: handle errors
|
||||
return this._send({ ...this._conf, text: message }).catch(logAndRethrow)
|
||||
|
||||
@@ -39,29 +39,31 @@
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-latest": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"cross-env": "^5.0.1",
|
||||
"dependency-check": "^2.9.1",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"clean": "rimraf dist/",
|
||||
"depcheck": "dependency-check ./package.json",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "yarn run depcheck",
|
||||
"prebuild": "yarn run clean",
|
||||
"predev": "yarn run clean",
|
||||
"prepublish": "yarn run build"
|
||||
"prepublishOnly": "yarn run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
"transform-runtime"
|
||||
],
|
||||
"presets": [
|
||||
"latest",
|
||||
"stage-0"
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,34 +9,34 @@ export const configurationSchema = {
|
||||
properties: {
|
||||
host: {
|
||||
type: 'string',
|
||||
description: 'host where the XMPP server is located'
|
||||
description: 'host where the XMPP server is located',
|
||||
},
|
||||
port: {
|
||||
type: 'integer',
|
||||
description: 'port of the XMPP server (default to 5222)',
|
||||
default: 5222
|
||||
default: 5222,
|
||||
},
|
||||
jid: {
|
||||
type: 'string',
|
||||
title: 'user',
|
||||
description: 'Xmpp address to use to authenticate'
|
||||
description: 'Xmpp address to use to authenticate',
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
description: 'password to use to authenticate'
|
||||
}
|
||||
description: 'password to use to authenticate',
|
||||
},
|
||||
},
|
||||
|
||||
additionalProperties: false,
|
||||
required: ['jid', 'password']
|
||||
required: ['jid', 'password'],
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
class TransportXmppPlugin {
|
||||
constructor ({ xo }) {
|
||||
this._sendToXmppClient = ::this._sendToXmppClient
|
||||
this._set = ::xo.defineProperty
|
||||
this._sendToXmppClient = this._sendToXmppClient.bind(this)
|
||||
this._set = xo.defineProperty.bind(xo)
|
||||
this._unset = null
|
||||
|
||||
// Defined in configure().
|
||||
@@ -73,7 +73,7 @@ class TransportXmppPlugin {
|
||||
this._client.send(
|
||||
new XmppClient.Stanza('message', {
|
||||
to: receiver,
|
||||
type: 'chat'
|
||||
type: 'chat',
|
||||
}).c('body').t(message)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"babel-runtime": "^6.23.0",
|
||||
"cron": "^1.2.1",
|
||||
"handlebars": "^4.0.6",
|
||||
"html-minifier": "^3.4.0",
|
||||
"html-minifier": "^3.5.8",
|
||||
"lodash": "^4.17.4",
|
||||
"promise-toolbox": "^0.9.5"
|
||||
},
|
||||
@@ -47,19 +47,16 @@
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.5.2",
|
||||
"babel-preset-stage-3": "^6.24.1",
|
||||
"cross-env": "^5.0.1",
|
||||
"dependency-check": "^2.9.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"clean": "rimraf dist/",
|
||||
"depcheck": "dependency-check ./package.json",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"posttest": "npm run depcheck",
|
||||
"prebuild": "npm run clean",
|
||||
"predev": "npm run clean",
|
||||
"prepublish": "npm run build"
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"babel": {
|
||||
"plugins": [
|
||||
|
||||
@@ -12,14 +12,14 @@ import {
|
||||
orderBy,
|
||||
round,
|
||||
values,
|
||||
zipObject
|
||||
zipObject,
|
||||
} from 'lodash'
|
||||
import {
|
||||
promisify
|
||||
promisify,
|
||||
} from 'promise-toolbox'
|
||||
import {
|
||||
readFile,
|
||||
writeFile
|
||||
writeFile,
|
||||
} from 'fs'
|
||||
|
||||
// ===================================================================
|
||||
@@ -30,10 +30,10 @@ const pWriteFile = promisify(writeFile)
|
||||
const currDate = new Date().toISOString().slice(0, 10)
|
||||
|
||||
const compareOperators = {
|
||||
'>': (l, r) => l > r
|
||||
'>': (l, r) => l > r,
|
||||
}
|
||||
const mathOperators = {
|
||||
'+': (l, r) => l + r
|
||||
'+': (l, r) => l + r,
|
||||
}
|
||||
|
||||
const gibPower = Math.pow(2, 30)
|
||||
@@ -50,7 +50,7 @@ pReadFile(`${__dirname}/../report.html.tpl`, 'utf8')
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true,
|
||||
removeOptionalTags: true,
|
||||
removeRedundantAttributes: true
|
||||
removeRedundantAttributes: true,
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -69,18 +69,18 @@ export const configurationSchema = {
|
||||
emails: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string'
|
||||
}
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
periodicity: {
|
||||
type: 'string',
|
||||
enum: ['monthly', 'weekly'],
|
||||
description: 'If you choose weekly you will receive the report every sunday and if you choose monthly you will receive it every first day of the month.'
|
||||
}
|
||||
description: 'If you choose weekly you will receive the report every sunday and if you choose monthly you will receive it every first day of the month.',
|
||||
},
|
||||
},
|
||||
|
||||
additionalProperties: false,
|
||||
required: [ 'emails', 'periodicity' ]
|
||||
required: [ 'emails', 'periodicity' ],
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
@@ -137,7 +137,7 @@ function computeMeans (objects, options) {
|
||||
options,
|
||||
map(
|
||||
options,
|
||||
opt => round(computeMean(map(objects, opt)), 2)
|
||||
opt => round(computeMean(map(objects, opt)), 2)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -156,7 +156,7 @@ function getTop (objects, options) {
|
||||
obj => ({
|
||||
uuid: obj.uuid,
|
||||
name: obj.name,
|
||||
value: round(obj[opt], 2)
|
||||
value: round(obj[opt], 2),
|
||||
})
|
||||
)
|
||||
)
|
||||
@@ -176,7 +176,7 @@ function conputePercentage (curr, prev, options) {
|
||||
function getDiff (oldElements, newElements) {
|
||||
return {
|
||||
added: differenceBy(oldElements, newElements, 'uuid'),
|
||||
removed: differenceBy(newElements, oldElements, 'uuid')
|
||||
removed: differenceBy(newElements, oldElements, 'uuid'),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ function getDiff (oldElements, newElements) {
|
||||
|
||||
function getVmsStats ({
|
||||
runningVms,
|
||||
xo
|
||||
xo,
|
||||
}) {
|
||||
return Promise.all(map(runningVms, async vm => {
|
||||
const vmStats = await xo.getXapiVmStats(vm, 'days')
|
||||
@@ -196,14 +196,14 @@ function getVmsStats ({
|
||||
diskRead: computeDoubleMean(values(vmStats.stats.xvds.r)) / mibPower,
|
||||
diskWrite: computeDoubleMean(values(vmStats.stats.xvds.w)) / mibPower,
|
||||
netReception: computeDoubleMean(vmStats.stats.vifs.rx) / kibPower,
|
||||
netTransmission: computeDoubleMean(vmStats.stats.vifs.tx) / kibPower
|
||||
netTransmission: computeDoubleMean(vmStats.stats.vifs.tx) / kibPower,
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
function getHostsStats ({
|
||||
runningHosts,
|
||||
xo
|
||||
xo,
|
||||
}) {
|
||||
return Promise.all(map(runningHosts, async host => {
|
||||
const hostStats = await xo.getXapiHostStats(host, 'days')
|
||||
@@ -214,7 +214,7 @@ function getHostsStats ({
|
||||
ram: computeMean(hostStats.stats.memoryUsed) / gibPower,
|
||||
load: computeMean(hostStats.stats.load),
|
||||
netReception: computeDoubleMean(hostStats.stats.pifs.rx) / kibPower,
|
||||
netTransmission: computeDoubleMean(hostStats.stats.pifs.tx) / kibPower
|
||||
netTransmission: computeDoubleMean(hostStats.stats.pifs.tx) / kibPower,
|
||||
}
|
||||
}))
|
||||
}
|
||||
@@ -222,76 +222,76 @@ function getHostsStats ({
|
||||
function computeGlobalVmsStats ({
|
||||
haltedVms,
|
||||
vmsStats,
|
||||
xo
|
||||
xo,
|
||||
}) {
|
||||
const allVms = concat(
|
||||
map(vmsStats, vm => ({
|
||||
uuid: vm.uuid,
|
||||
name: vm.name
|
||||
name: vm.name,
|
||||
})),
|
||||
map(haltedVms, vm => ({
|
||||
uuid: vm.uuid,
|
||||
name: vm.name_label
|
||||
name: vm.name_label,
|
||||
}))
|
||||
)
|
||||
|
||||
return assign(computeMeans(vmsStats, ['cpu', 'ram', 'diskRead', 'diskWrite', 'netReception', 'netTransmission']), {
|
||||
number: allVms.length,
|
||||
allVms
|
||||
allVms,
|
||||
})
|
||||
}
|
||||
|
||||
function computeGlobalHostsStats ({
|
||||
haltedHosts,
|
||||
hostsStats,
|
||||
xo
|
||||
xo,
|
||||
}) {
|
||||
const allHosts = concat(
|
||||
map(hostsStats, host => ({
|
||||
uuid: host.uuid,
|
||||
name: host.name
|
||||
name: host.name,
|
||||
})),
|
||||
map(haltedHosts, host => ({
|
||||
uuid: host.uuid,
|
||||
name: host.name_label
|
||||
name: host.name_label,
|
||||
}))
|
||||
)
|
||||
|
||||
return assign(computeMeans(hostsStats, ['cpu', 'ram', 'load', 'netReception', 'netTransmission']), {
|
||||
number: allHosts.length,
|
||||
allHosts
|
||||
allHosts,
|
||||
})
|
||||
}
|
||||
|
||||
function getTopVms ({
|
||||
vmsStats,
|
||||
xo
|
||||
xo,
|
||||
}) {
|
||||
return getTop(vmsStats, ['cpu', 'ram', 'diskRead', 'diskWrite', 'netReception', 'netTransmission'])
|
||||
}
|
||||
|
||||
function getTopHosts ({
|
||||
hostsStats,
|
||||
xo
|
||||
xo,
|
||||
}) {
|
||||
return getTop(hostsStats, ['cpu', 'ram', 'load', 'netReception', 'netTransmission'])
|
||||
}
|
||||
|
||||
function getMostAllocatedSpaces ({
|
||||
disks,
|
||||
xo
|
||||
xo,
|
||||
}) {
|
||||
return map(
|
||||
orderBy(disks, ['size'], ['desc']).slice(0, 3), disk => ({
|
||||
uuid: disk.uuid,
|
||||
name: disk.name_label,
|
||||
size: round(disk.size / gibPower, 2)
|
||||
size: round(disk.size / gibPower, 2),
|
||||
}))
|
||||
}
|
||||
|
||||
async function getHostsMissingPatches ({
|
||||
runningHosts,
|
||||
xo
|
||||
xo,
|
||||
}) {
|
||||
const hostsMissingPatches = await Promise.all(map(runningHosts, async host => {
|
||||
const hostsPatches = await xo.getXapi(host).listMissingPoolPatchesOnHost(host._xapiId)
|
||||
@@ -299,7 +299,7 @@ async function getHostsMissingPatches ({
|
||||
return {
|
||||
uuid: host.uuid,
|
||||
name: host.name_label,
|
||||
patches: map(hostsPatches, 'name')
|
||||
patches: map(hostsPatches, 'name'),
|
||||
}
|
||||
}
|
||||
}))
|
||||
@@ -312,7 +312,7 @@ function getAllUsersEmail (users) {
|
||||
|
||||
async function storeStats ({
|
||||
data,
|
||||
storedStatsPath
|
||||
storedStatsPath,
|
||||
}) {
|
||||
await pWriteFile(storedStatsPath, JSON.stringify(data))
|
||||
}
|
||||
@@ -332,12 +332,12 @@ async function computeEvolution ({
|
||||
|
||||
const vmsEvolution = {
|
||||
number: newStatsVms.number - oldStatsVms.number,
|
||||
...conputePercentage(newStatsVms, oldStatsVms, ['cpu', 'ram', 'diskRead', 'diskWrite', 'netReception', 'netTransmission'])
|
||||
...conputePercentage(newStatsVms, oldStatsVms, ['cpu', 'ram', 'diskRead', 'diskWrite', 'netReception', 'netTransmission']),
|
||||
}
|
||||
|
||||
const hostsEvolution = {
|
||||
number: newStatsHosts.number - oldStatsHosts.number,
|
||||
...conputePercentage(newStatsHosts, oldStatsHosts, ['cpu', 'ram', 'load', 'netReception', 'netTransmission'])
|
||||
...conputePercentage(newStatsHosts, oldStatsHosts, ['cpu', 'ram', 'load', 'netReception', 'netTransmission']),
|
||||
}
|
||||
|
||||
const vmsRessourcesEvolution = getDiff(oldStatsVms.allVms, newStatsVms.allVms)
|
||||
@@ -351,7 +351,7 @@ async function computeEvolution ({
|
||||
prevDate,
|
||||
vmsRessourcesEvolution,
|
||||
hostsRessourcesEvolution,
|
||||
usersEvolution
|
||||
usersEvolution,
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') throw err
|
||||
@@ -360,7 +360,7 @@ async function computeEvolution ({
|
||||
|
||||
async function dataBuilder ({
|
||||
xo,
|
||||
storedStatsPath
|
||||
storedStatsPath,
|
||||
}) {
|
||||
const xoObjects = values(xo.getObjects())
|
||||
const runningVms = filter(xoObjects, {type: 'VM', power_state: 'Running'})
|
||||
@@ -373,7 +373,7 @@ async function dataBuilder ({
|
||||
getVmsStats({xo, runningVms}),
|
||||
getHostsStats({xo, runningHosts}),
|
||||
getMostAllocatedSpaces({xo, disks}),
|
||||
getHostsMissingPatches({xo, runningHosts})
|
||||
getHostsMissingPatches({xo, runningHosts}),
|
||||
])
|
||||
|
||||
const [globalVmsStats, globalHostsStats, topVms, topHosts, usersEmail] = await Promise.all([
|
||||
@@ -381,13 +381,13 @@ async function dataBuilder ({
|
||||
computeGlobalHostsStats({xo, hostsStats, haltedHosts}),
|
||||
getTopVms({xo, vmsStats}),
|
||||
getTopHosts({xo, hostsStats}),
|
||||
getAllUsersEmail(users)
|
||||
getAllUsersEmail(users),
|
||||
])
|
||||
const evolution = await computeEvolution({
|
||||
storedStatsPath,
|
||||
hosts: globalHostsStats,
|
||||
usersEmail,
|
||||
vms: globalVmsStats
|
||||
vms: globalVmsStats,
|
||||
})
|
||||
|
||||
const data = {
|
||||
@@ -395,7 +395,7 @@ async function dataBuilder ({
|
||||
vms: globalVmsStats,
|
||||
hosts: globalHostsStats,
|
||||
vmsEvolution: evolution && evolution.vmsEvolution,
|
||||
hostsEvolution: evolution && evolution.hostsEvolution
|
||||
hostsEvolution: evolution && evolution.hostsEvolution,
|
||||
},
|
||||
topVms,
|
||||
topHosts,
|
||||
@@ -409,8 +409,8 @@ async function dataBuilder ({
|
||||
imgXo,
|
||||
currDate,
|
||||
prevDate: evolution && evolution.prevDate,
|
||||
page: '{{page}}'
|
||||
}
|
||||
page: '{{page}}',
|
||||
},
|
||||
}
|
||||
|
||||
return data
|
||||
@@ -432,7 +432,7 @@ class UsageReportPlugin {
|
||||
this._job = new CronJob({
|
||||
cronTime: configuration.periodicity === 'monthly' ? '00 06 1 * *' : '00 06 * * 0',
|
||||
onTick: () => this._sendReport(),
|
||||
start: false
|
||||
start: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -454,7 +454,7 @@ class UsageReportPlugin {
|
||||
async _sendReport () {
|
||||
const data = await dataBuilder({
|
||||
xo: this._xo,
|
||||
storedStatsPath: this._storedStatsPath
|
||||
storedStatsPath: this._storedStatsPath,
|
||||
})
|
||||
|
||||
await Promise.all([
|
||||
@@ -469,13 +469,13 @@ class UsageReportPlugin {
|
||||
best regards.`,
|
||||
attachments: [{
|
||||
filename: `xoReport_${currDate}.html`,
|
||||
content: template(data)
|
||||
}]
|
||||
content: template(data),
|
||||
}],
|
||||
}),
|
||||
storeStats({
|
||||
data,
|
||||
storedStatsPath: this._storedStatsPath
|
||||
})
|
||||
storedStatsPath: this._storedStatsPath,
|
||||
}),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
language: node_js
|
||||
sudo: required
|
||||
dist: trusty
|
||||
node_js:
|
||||
- stable
|
||||
- 6
|
||||
- 4
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
- uuid-dev
|
||||
- build-essential
|
||||
- qemu-utils
|
||||
install:
|
||||
- sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu/ trusty multiverse"
|
||||
- sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu/ trusty-updates multiverse"
|
||||
- sudo apt-get update
|
||||
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install virtualbox
|
||||
- npm install
|
||||
- wget https://github.com/rubiojr/vhd-util-convert/archive/master.tar.gz -O /tmp/vhd-util-convert.tar.gz
|
||||
- tar -xvf /tmp/vhd-util-convert.tar.gz
|
||||
- (cd vhd-util-convert-master && make)
|
||||
- df
|
||||
- pwd
|
||||
- echo "$TRAVIS_BUILD_DIR"
|
||||
script:
|
||||
- npm test
|
||||
- find $TRAVIS_BUILD_DIR -name '*.vhd' -print0 | xargs -n 1 -0 ./vhd-util-convert-master/vhd-util scan -v
|
||||
@@ -33,9 +33,7 @@
|
||||
"babel-cli": "^6.18.0",
|
||||
"babel-plugin-transform-runtime": "^6.15.0",
|
||||
"babel-preset-env": "^1.0.0",
|
||||
"babel-preset-stage-0": "^6.16.0",
|
||||
"cross-env": "^5.1.1",
|
||||
"dependency-check": "^2.6.0",
|
||||
"cross-env": "^5.1.3",
|
||||
"mocha": "^4.0.1",
|
||||
"must": "^0.13.2",
|
||||
"rimraf": "^2.5.4"
|
||||
@@ -43,13 +41,11 @@
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"clean": "rimraf dist/",
|
||||
"depcheck": "dependency-check ./package.json",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"dev-test": "mocha --watch --reporter=min \"dist/**/*.spec.js\"",
|
||||
"posttest": "npm run depcheck",
|
||||
"prebuild": "npm run clean",
|
||||
"predev": "npm run clean",
|
||||
"prepublish": "npm run build",
|
||||
"prepublishOnly": "npm run build",
|
||||
"test-disabled": "mocha \"dist/**/*.spec.js\""
|
||||
},
|
||||
"babel": {
|
||||
@@ -64,8 +60,7 @@
|
||||
"node": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"stage-0"
|
||||
]
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
|
||||
@@ -124,7 +124,7 @@ export class VHDFile {
|
||||
}
|
||||
|
||||
export function computeGeometryForSize (size) {
|
||||
let totalSectors = Math.ceil(size / 512)
|
||||
const totalSectors = Math.ceil(size / 512)
|
||||
let sectorsPerTrack
|
||||
let heads
|
||||
let cylinderTimesHeads
|
||||
@@ -154,13 +154,13 @@ export function computeGeometryForSize (size) {
|
||||
cylinderTimesHeads = totalSectors / sectorsPerTrack
|
||||
}
|
||||
}
|
||||
let cylinders = Math.floor(cylinderTimesHeads / heads)
|
||||
let actualSize = cylinders * heads * sectorsPerTrack * sectorSize
|
||||
const cylinders = Math.floor(cylinderTimesHeads / heads)
|
||||
const actualSize = cylinders * heads * sectorsPerTrack * sectorSize
|
||||
return {cylinders, heads, sectorsPerTrack, actualSize}
|
||||
}
|
||||
|
||||
export function createFooter (size, timestamp, geometry, diskType, dataOffsetLow = 0xFFFFFFFF, dataOffsetHigh = 0xFFFFFFFF) {
|
||||
let footer = Buffer.alloc(512)
|
||||
const footer = Buffer.alloc(512)
|
||||
Buffer.from(footerCookie, 'ascii').copy(footer)
|
||||
footer.writeUInt32BE(2, 8)
|
||||
footer.writeUInt32BE(0x00010000, 12)
|
||||
@@ -180,13 +180,13 @@ export function createFooter (size, timestamp, geometry, diskType, dataOffsetLow
|
||||
footer.writeUInt8(geometry['heads'], 58)
|
||||
footer.writeUInt8(geometry['sectorsPerTrack'], 59)
|
||||
footer.writeUInt32BE(diskType, 60)
|
||||
let checksum = computeChecksum(footer)
|
||||
const checksum = computeChecksum(footer)
|
||||
footer.writeUInt32BE(checksum, 64)
|
||||
return footer
|
||||
}
|
||||
|
||||
export function createDynamicDiskHeader (tableEntries, blockSize) {
|
||||
let header = Buffer.alloc(1024)
|
||||
const header = Buffer.alloc(1024)
|
||||
Buffer.from(headerCookie, 'ascii').copy(header)
|
||||
// hard code no next data
|
||||
header.writeUInt32BE(0xFFFFFFFF, 8)
|
||||
@@ -197,7 +197,7 @@ export function createDynamicDiskHeader (tableEntries, blockSize) {
|
||||
header.writeUInt32BE(0x00010000, 24)
|
||||
header.writeUInt32BE(tableEntries, 28)
|
||||
header.writeUInt32BE(blockSize, 32)
|
||||
let checksum = computeChecksum(header)
|
||||
const checksum = computeChecksum(header)
|
||||
header.writeUInt32BE(checksum, 36)
|
||||
return header
|
||||
}
|
||||
@@ -213,7 +213,7 @@ export class ReadableRawVHDStream extends stream.Readable {
|
||||
constructor (size, vmdkParser) {
|
||||
super()
|
||||
this.size = size
|
||||
var geometry = computeGeometryForSize(size)
|
||||
const geometry = computeGeometryForSize(size)
|
||||
this.footer = createFooter(size, Math.floor(Date.now() / 1000), geometry, fixedHardDiskType)
|
||||
this.position = 0
|
||||
this.vmdkParser = vmdkParser
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
createDynamicDiskHeader,
|
||||
createFooter,
|
||||
ReadableRawVHDStream,
|
||||
VHDFile
|
||||
VHDFile,
|
||||
} from './vhd-write'
|
||||
|
||||
describe('VHD writing', () => {
|
||||
@@ -37,10 +37,10 @@ describe('VHD writing', () => {
|
||||
it('ReadableRawVHDStream does not crash', () => {
|
||||
const data = [{
|
||||
lbaBytes: 100,
|
||||
grain: Buffer.from('azerzaerazeraze', 'ascii')
|
||||
grain: Buffer.from('azerzaerazeraze', 'ascii'),
|
||||
}, {
|
||||
lbaBytes: 700,
|
||||
grain: Buffer.from('gdfslkdfguer', 'ascii')
|
||||
grain: Buffer.from('gdfslkdfguer', 'ascii'),
|
||||
}]
|
||||
let index = 0
|
||||
const mockParser = {
|
||||
@@ -52,7 +52,7 @@ describe('VHD writing', () => {
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
const stream = new ReadableRawVHDStream(100000, mockParser)
|
||||
const pipe = stream.pipe(createWriteStream('outputStream'))
|
||||
@@ -65,10 +65,10 @@ describe('VHD writing', () => {
|
||||
it('ReadableRawVHDStream detects when blocks are out of order', () => {
|
||||
const data = [{
|
||||
lbaBytes: 700,
|
||||
grain: Buffer.from('azerzaerazeraze', 'ascii')
|
||||
grain: Buffer.from('azerzaerazeraze', 'ascii'),
|
||||
}, {
|
||||
lbaBytes: 100,
|
||||
grain: Buffer.from('gdfslkdfguer', 'ascii')
|
||||
grain: Buffer.from('gdfslkdfguer', 'ascii'),
|
||||
}]
|
||||
let index = 0
|
||||
const mockParser = {
|
||||
@@ -80,7 +80,7 @@ describe('VHD writing', () => {
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
return expect(new Promise((resolve, reject) => {
|
||||
const stream = new ReadableRawVHDStream(100000, mockParser)
|
||||
|
||||
@@ -7,7 +7,7 @@ import {VirtualBuffer} from './virtual-buffer'
|
||||
|
||||
describe('Virtual Buffer', function () {
|
||||
it('can read a file correctly', async () => {
|
||||
let rawFileName = 'random-data'
|
||||
const rawFileName = 'random-data'
|
||||
await exec('base64 /dev/urandom | head -c 104448 > ' + rawFileName)
|
||||
const buffer = new VirtualBuffer(createReadStream(rawFileName))
|
||||
const part1 = await buffer.readChunk(10)
|
||||
|
||||
@@ -37,8 +37,8 @@ function parseDescriptor (descriptorSlice) {
|
||||
const lines = descriptorText.split(/\r?\n/).filter((line) => {
|
||||
return line.trim().length > 0 && line[0] !== '#'
|
||||
})
|
||||
for (let line of lines) {
|
||||
let defLine = line.split('=')
|
||||
for (const line of lines) {
|
||||
const defLine = line.split('=')
|
||||
// the wonky quote test is to avoid having an equal sign in the name of an extent
|
||||
if (defLine.length === 2 && defLine[0].indexOf('"') === -1) {
|
||||
descriptorDict[defLine[0]] = defLine[1].replace(/['"]+/g, '')
|
||||
@@ -49,7 +49,7 @@ function parseDescriptor (descriptorSlice) {
|
||||
sizeSectors: items[1],
|
||||
type: items[2],
|
||||
name: items[3],
|
||||
offset: items.length > 4 ? items[4] : 0
|
||||
offset: items.length > 4 ? items[4] : 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ function parseFlags (flagBuffer) {
|
||||
useSecondaryGrain: !!(number & (1 << 1)),
|
||||
useZeroedGrainTable: !!(number & (1 << 2)),
|
||||
compressedGrains: !!(number & (1 << 16)),
|
||||
hasMarkers: !!(number & (1 << 17))
|
||||
hasMarkers: !!(number & (1 << 17)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ function parseHeader (buffer) {
|
||||
grainDirectoryOffsetSectors,
|
||||
rGrainDirectoryOffsetSectors,
|
||||
l1EntrySectors,
|
||||
numGTEsPerGT
|
||||
numGTEsPerGT,
|
||||
}
|
||||
}
|
||||
async function readGrain (offsetSectors, buffer, compressed) {
|
||||
@@ -115,7 +115,7 @@ async function readGrain (offsetSectors, buffer, compressed) {
|
||||
size,
|
||||
buffer: grainBuffer,
|
||||
grain: grainContent,
|
||||
grainSize: grainContent.byteLength
|
||||
grainSize: grainContent.byteLength,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import {VMDKDirectParser} from './vmdk-read'
|
||||
|
||||
describe('VMDK reading', () => {
|
||||
it('VMDKDirectParser reads OK', async () => {
|
||||
let rawFileName = 'random-data'
|
||||
let fileName = 'random-data.vmdk'
|
||||
const rawFileName = 'random-data'
|
||||
const fileName = 'random-data.vmdk'
|
||||
await exec('base64 /dev/urandom | head -c 104448 > ' + rawFileName)
|
||||
await exec('rm -f ' + fileName + '&& VBoxManage convertfromraw --format VMDK --variant Stream ' + rawFileName + ' ' + fileName)
|
||||
const parser = new VMDKDirectParser(createReadStream(fileName))
|
||||
|
||||
@@ -9,11 +9,11 @@ import {VHDFile, convertFromVMDK, computeGeometryForSize} from './vhd-write'
|
||||
|
||||
describe('VMDK to VHD conversion', () => {
|
||||
it('can convert a random data file with readRawContent()', async () => {
|
||||
let inputRawFileName = 'random-data.raw'
|
||||
let vmdkFileName = 'random-data.vmdk'
|
||||
let vhdFileName = 'from-vmdk-readRawContent.vhd'
|
||||
let reconvertedRawFilemane = 'from-vhd.raw'
|
||||
let dataSize = 5222400
|
||||
const inputRawFileName = 'random-data.raw'
|
||||
const vmdkFileName = 'random-data.vmdk'
|
||||
const vhdFileName = 'from-vmdk-readRawContent.vhd'
|
||||
const reconvertedRawFilemane = 'from-vhd.raw'
|
||||
const dataSize = 5222400
|
||||
await exec('rm -f ' + [inputRawFileName, vmdkFileName, vhdFileName, reconvertedRawFilemane].join(' '))
|
||||
await exec('base64 /dev/urandom | head -c ' + dataSize + ' > ' + inputRawFileName)
|
||||
await exec('VBoxManage convertfromraw --format VMDK --variant Stream ' + inputRawFileName + ' ' + vmdkFileName)
|
||||
@@ -33,12 +33,12 @@ describe('VMDK to VHD conversion', () => {
|
||||
})
|
||||
|
||||
it('can convert a random data file with VMDKDirectParser', async () => {
|
||||
let inputRawFileName = 'random-data.raw'
|
||||
let vmdkFileName = 'random-data.vmdk'
|
||||
let vhdFileName = 'from-vmdk-VMDKDirectParser.vhd'
|
||||
let reconvertedRawFilemane = 'from-vhd.raw'
|
||||
let reconvertedByVBoxRawFilemane = 'from-vhd-by-vbox.raw'
|
||||
let dataSize = computeGeometryForSize(8 * 1024 * 1024).actualSize
|
||||
const inputRawFileName = 'random-data.raw'
|
||||
const vmdkFileName = 'random-data.vmdk'
|
||||
const vhdFileName = 'from-vmdk-VMDKDirectParser.vhd'
|
||||
const reconvertedRawFilemane = 'from-vhd.raw'
|
||||
const reconvertedByVBoxRawFilemane = 'from-vhd-by-vbox.raw'
|
||||
const dataSize = computeGeometryForSize(8 * 1024 * 1024).actualSize
|
||||
await exec('rm -f ' + [inputRawFileName, vmdkFileName, vhdFileName, reconvertedRawFilemane, reconvertedByVBoxRawFilemane].join(' '))
|
||||
await exec('base64 /dev/urandom | head -c ' + dataSize + ' > ' + inputRawFileName)
|
||||
await exec('VBoxManage convertfromraw --format VMDK --variant Stream ' + inputRawFileName + ' ' + vmdkFileName)
|
||||
|
||||
@@ -1,27 +1,83 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Import repositories into this mono-repo.
|
||||
#
|
||||
# For each repository:
|
||||
#
|
||||
# 1. rewrite history, moving all files into packages/<repository>
|
||||
# 2. rename all tags: <tag> → <repository>-<tag>
|
||||
# 3. merge with master
|
||||
for pkg
|
||||
set -eu
|
||||
|
||||
usage () {
|
||||
echo "$0 [--commit | --rewrite] <repository url>..."
|
||||
}
|
||||
|
||||
move=commit
|
||||
while [ $# -ne 0 ]
|
||||
do
|
||||
[ -d "packages/$pkg" ] || { {
|
||||
[ -f ".git/refs/remotes/$pkg/master" ] || \
|
||||
git remote add "$pkg" "https://github.com/vatesfr/$pkg"
|
||||
} && {
|
||||
git fetch --no-tags "$pkg" master refs/tags/*:refs/tags/"$pkg"-* && \
|
||||
git filter-branch -f --index-filter '
|
||||
git ls-files -s | \
|
||||
sed "s%\t\"*%&packages/'"$pkg"'/%" | \
|
||||
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
|
||||
git update-index --index-info && \
|
||||
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
|
||||
' --tag-name-filter 'cat' "$pkg/master" && \
|
||||
git merge --allow-unrelated-histories "$pkg/master"
|
||||
git remote rm "$pkg"
|
||||
} }
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
usage
|
||||
exit
|
||||
;;
|
||||
--commit)
|
||||
move=commit
|
||||
;;
|
||||
--rewrite)
|
||||
move=rewrite
|
||||
;;
|
||||
-*)
|
||||
echo "Invalid argument: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Create a commit in branch <package>/master moving all files in <path>.
|
||||
move_commit () {
|
||||
local head
|
||||
head=$(git symbolic-ref HEAD)
|
||||
|
||||
git symbolic-ref HEAD "refs/heads/$1/master"
|
||||
|
||||
rm -f "$(git rev-parse --git-path index)"
|
||||
git read-tree --prefix="$2/" HEAD
|
||||
git commit -nm "feat($1): move all files to $2"
|
||||
|
||||
git symbolic-ref HEAD "$head"
|
||||
git read-tree HEAD
|
||||
}
|
||||
|
||||
# Rewrite all branches <package>/* moving all files in <path>.
|
||||
move_rewrite () {
|
||||
git filter-branch -f --index-filter '
|
||||
git ls-files -s | \
|
||||
sed "s%\t\"*%&'"$2"'/%" | \
|
||||
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
|
||||
git update-index --index-info && \
|
||||
if [ -f "$GIT_INDEX_FILE.new" ]; then \
|
||||
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"; \
|
||||
fi
|
||||
' --tag-name-filter 'cat' -- --branches="$1/*"
|
||||
}
|
||||
|
||||
for url
|
||||
do
|
||||
pkg=$(basename "$url" .git)
|
||||
if [ -d "packages/$pkg" ]
|
||||
then
|
||||
continue
|
||||
fi
|
||||
|
||||
# import all branches and tags as <repository>/*
|
||||
git remote add "$pkg" "$url"
|
||||
git fetch --no-tags "$pkg" \
|
||||
"refs/heads/*:refs/heads/$pkg/*" \
|
||||
"refs/tags/*:refs/tags/$pkg/*"
|
||||
git remote rm "$pkg"
|
||||
|
||||
move_"$move" "$pkg" "packages/$pkg"
|
||||
|
||||
# merge and delete master branch
|
||||
git merge --allow-unrelated-histories "$pkg/master"
|
||||
git branch -d "$pkg/master"
|
||||
done
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const isEmpty = require('lodash/isEmpty')
|
||||
const sortedObject = require('sorted-object')
|
||||
const { getPackages, writeFile, unlink } = require('./utils')
|
||||
|
||||
const deleteProperties = (object, property, properties) => {
|
||||
const nestedObject = object[property]
|
||||
if (nestedObject === undefined) {
|
||||
return
|
||||
}
|
||||
properties.forEach(property => {
|
||||
delete nestedObject[property]
|
||||
})
|
||||
if (isEmpty(object[property])) {
|
||||
delete object[property]
|
||||
}
|
||||
}
|
||||
|
||||
require('exec-promise')(() =>
|
||||
getPackages(true).map(({ dir, name, package: pkg }) => {
|
||||
pkg.name = name
|
||||
@@ -9,24 +24,44 @@ require('exec-promise')(() =>
|
||||
pkg.bugs = `https://github.com/vatesfr/xo-web/issues`
|
||||
pkg.repository = {
|
||||
type: 'git',
|
||||
url: 'https://github.com/vatesfr/xen-orchestra.git'
|
||||
url: 'https://github.com/vatesfr/xen-orchestra.git',
|
||||
}
|
||||
|
||||
delete pkg.jest
|
||||
delete pkg.standard
|
||||
|
||||
const { devDependencies, scripts } = pkg
|
||||
if (devDependencies !== undefined) {
|
||||
delete devDependencies.commitizen
|
||||
delete devDependencies.ghooks
|
||||
delete devDependencies.husky
|
||||
delete devDependencies.jest
|
||||
delete devDependencies.standard
|
||||
delete devDependencies['babel-eslint']
|
||||
delete devDependencies['cz-conventional-changelog']
|
||||
}
|
||||
deleteProperties(pkg, 'config', [ 'commitizen' ])
|
||||
deleteProperties(pkg, 'devDependencies', [
|
||||
'babel-7-jest',
|
||||
'babel-eslint',
|
||||
'commitizen',
|
||||
'cz-conventional-changelog',
|
||||
'dependency-check',
|
||||
'eslint',
|
||||
'eslint-config-standard',
|
||||
'eslint-plugin-import',
|
||||
'eslint-plugin-node',
|
||||
'eslint-plugin-promise',
|
||||
'eslint-plugin-standard',
|
||||
'flow-bin',
|
||||
'ghooks',
|
||||
'husky',
|
||||
'jest',
|
||||
'standard',
|
||||
])
|
||||
deleteProperties(pkg, 'scripts', [ 'commitmsg', 'cz' ])
|
||||
|
||||
const { scripts } = pkg
|
||||
if (scripts !== undefined) {
|
||||
delete scripts.cz
|
||||
const prepublish = scripts.prepublish
|
||||
if (
|
||||
prepublish !== undefined &&
|
||||
!('prepublishOnly' in scripts)
|
||||
) {
|
||||
delete scripts.prepublish
|
||||
scripts.prepublishOnly = prepublish
|
||||
pkg.scripts = sortedObject(scripts)
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
@@ -35,9 +70,13 @@ require('exec-promise')(() =>
|
||||
JSON.stringify(pkg, null, 2) + '\n'
|
||||
),
|
||||
unlink(`${dir}/.editorconfig`),
|
||||
unlink(`${dir}/.eslintrc.js`),
|
||||
unlink(`${dir}/.flowconfig`),
|
||||
unlink(`${dir}/.gitignore`),
|
||||
unlink(`${dir}/.jshintrc`),
|
||||
unlink(`${dir}/yarn.lock`)
|
||||
unlink(`${dir}/.travis.yml`),
|
||||
unlink(`${dir}/package-lock.json`),
|
||||
unlink(`${dir}/yarn.lock`),
|
||||
])
|
||||
})
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user