Merge pull request #1221 from balena-io/lint-js

Update to balena-lint and enable javascript linting
This commit is contained in:
Page- 2020-03-24 11:18:47 +00:00 committed by GitHub
commit 198c1a8fab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 597 additions and 472 deletions

View File

@ -2,7 +2,7 @@
"*.coffee": [
"resin-lint"
],
"*.ts": [
"*.ts$|!(*webpack.config).js$": [
"resin-lint --typescript --fix",
],
"test/**/*.coffee": [

View File

@ -66,7 +66,7 @@ COPY src /usr/src/app/src
COPY test /usr/src/app/test
COPY typings /usr/src/app/typings
RUN npm test \
RUN npm run test-nolint \
&& npm run build
##############################################################################

View File

@ -24,8 +24,7 @@ defaults: &defaults
name: Install npm dependencies
working_directory: /tmp/build/automation
command: |
JOBS=max npm install \
&& npm cache clean --force
JOBS=max npm install
- run:
name: Initialize the submodules (yocto layers)
command: |
@ -62,6 +61,22 @@ defaults: &defaults
version: 2
jobs:
generic:
docker:
- image: library/node:10
steps:
- checkout
- run:
name: Run tests
command: |
JOBS=max npm ci && npm test
environment:
DOCKER_USERNAME: travisciresin
ARCH: amd64
PUSH_IMAGES: 'true'
STAGING_API_ENDPOINT: https://api.balena-staging.com
PRODUCTION_API_ENDPOINT: https://api.balena-cloud.com
DEBUG: ''
amd64:
<<: *defaults
environment:
@ -175,15 +190,40 @@ workflows:
version: 2
build_and_maybe_deploy:
jobs:
- amd64
- i386
- rpi
- armv7hf
- aarch64
- i386-nlp
- amd64-debug
- i386-debug
- rpi-debug
- armv7hf-debug
- aarch64-debug
- i386-nlp-debug
- generic
- amd64:
requires:
- generic
- i386:
requires:
- generic
- rpi:
requires:
- generic
- armv7hf:
requires:
- generic
- aarch64:
requires:
- generic
- i386-nlp:
requires:
- generic
- amd64-debug:
requires:
- generic
- i386-debug:
requires:
- generic
- rpi-debug:
requires:
- generic
- armv7hf-debug:
requires:
- generic
- aarch64-debug:
requires:
- generic
- i386-nlp-debug:
requires:
- generic

129
package-lock.json generated
View File

@ -82,6 +82,71 @@
}
}
},
"@balena/lint": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@balena/lint/-/lint-4.1.0.tgz",
"integrity": "sha512-5db6EhVYblBh70J8k9LZYww1ntd3xwL/4hkysyB70itS/wBQLIMqGIVaiqfpayN61mYdbs24Fm5ijgDkhT4OLQ==",
"dev": true,
"requires": {
"@types/depcheck": "^0.6.0",
"@types/glob": "^7.1.1",
"@types/lodash": "^4.14.149",
"@types/node": "^8.10.59",
"@types/optimist": "0.0.29",
"@types/prettier": "^1.18.3",
"coffee-script": "^1.10.0",
"coffeelint": "^1.15.0",
"coffeescope2": "^0.4.5",
"depcheck": "^0.6.7",
"glob": "^7.1.6",
"lodash": "^4.17.15",
"optimist": "^0.6.1",
"prettier": "^1.19.1",
"tslint": "^5.20.1",
"tslint-config-prettier": "^1.18.0",
"tslint-no-unused-expression-chai": "^0.1.4",
"typescript": "^3.7.5"
},
"dependencies": {
"@types/glob": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
"integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==",
"dev": true,
"requires": {
"@types/events": "*",
"@types/minimatch": "*",
"@types/node": "*"
}
},
"@types/node": {
"version": "8.10.59",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.59.tgz",
"integrity": "sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==",
"dev": true
},
"coffee-script": {
"version": "1.12.7",
"resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz",
"integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==",
"dev": true
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
}
}
},
"@resin.io/types-hidepath": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@resin.io/types-hidepath/-/types-hidepath-1.0.1.tgz",
@ -398,9 +463,9 @@
"dev": true
},
"@types/prettier": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.19.0.tgz",
"integrity": "sha512-gDE8JJEygpay7IjA/u3JiIURvwZW08f0cZSZLAzFoX/ZmeqvS0Sqv+97aKuHpNsalAMMhwPe+iAS6fQbfmbt7A==",
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.19.1.tgz",
"integrity": "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==",
"dev": true
},
"@types/range-parser": {
@ -6363,7 +6428,7 @@
"dependencies": {
"ansi-escapes": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
"resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
"integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=",
"dev": true
},
@ -7059,7 +7124,7 @@
},
"chalk": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
"resolved": "http://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
"integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
"dev": true,
"requires": {
@ -9673,60 +9738,6 @@
"lodash": "^4.0.0"
}
},
"resin-lint": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/resin-lint/-/resin-lint-3.2.4.tgz",
"integrity": "sha512-VtsEAG8fgSkZMnZ2CPLtukEqJMjFBSya8T//AjUHGEqFr/M8aUfdSZvIXq8vVFholaCMW84XBmVu9zWK8H0arg==",
"dev": true,
"requires": {
"@types/depcheck": "^0.6.0",
"@types/glob": "^5.0.35",
"@types/lodash": "^4.14.149",
"@types/node": "^8.10.59",
"@types/optimist": "0.0.29",
"@types/prettier": "^1.18.3",
"coffee-script": "^1.10.0",
"coffeelint": "^1.15.0",
"coffeescope2": "^0.4.5",
"depcheck": "^0.6.7",
"glob": "^7.1.6",
"lodash": "^4.17.15",
"optimist": "^0.6.1",
"prettier": "^1.19.1",
"tslint": "^5.20.1",
"tslint-config-prettier": "^1.18.0",
"tslint-no-unused-expression-chai": "^0.1.4",
"typescript": "^3.7.5"
},
"dependencies": {
"@types/node": {
"version": "8.10.59",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.59.tgz",
"integrity": "sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==",
"dev": true
},
"coffee-script": {
"version": "1.12.7",
"resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz",
"integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==",
"dev": true
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
}
}
},
"resin-register-device": {
"version": "3.0.0",
"resolved": "http://registry.npmjs.org/resin-register-device/-/resin-register-device-3.0.0.tgz",

View File

@ -9,25 +9,24 @@
},
"scripts": {
"start": "./entry.sh",
"build": "webpack",
"build:debug": "npm run typescript:release && npm run coffeescript:release && npm run migrations:copy && npm run packagejson:copy",
"build": "npm run typescript:release && webpack",
"build:debug": "npm run typescript:release && npm run coffeescript:release && npm run packagejson:copy",
"lint": "npm run lint:coffee && npm run lint:typescript",
"test": "npm run lint && npm run test:build && TEST=1 JUNIT_REPORT_PATH=report.xml istanbul cover _mocha && npm run coverage",
"test:build": "npm run typescript:test-build && npm run coffeescript:test && npm run testitems:copy && npm run migrations:copy-test && npm run packagejson:copy",
"test": "npm run lint && npm run test-nolint",
"test-nolint": "npm run test:build && TEST=1 JUNIT_REPORT_PATH=report.xml istanbul cover _mocha && npm run coverage",
"test:build": "npm run typescript:test-build && npm run coffeescript:test && npm run testitems:copy && npm run packagejson:copy",
"coverage": "istanbul report text && istanbul report html",
"test:fast": "TEST=1 mocha --opts test/fast-mocha.opts",
"test:debug": "npm run test:build && TEST=1 mocha --inspect-brk",
"prettify": "resin-lint --typescript --fix src/ test/ typings/",
"prettify": "balena-lint -e ts -e js --typescript --fix src/ test/ typings/ webpack.config.js",
"typescript:test-build": "tsc --project tsconfig.json",
"typescript:release": "tsc --project tsconfig.release.json && cp -r build/src/* build && rm -rf build/src",
"coffeescript:test": "coffee -m -c -o build .",
"coffeescript:release": "coffee -m -c -o build src",
"migrations:copy": "cp -r src/migrations build/",
"migrations:copy-test": "cp -r src/migrations build/src",
"packagejson:copy": "cp package.json build/",
"testitems:copy": "cp -r test/data build/test/",
"lint:coffee": "resin-lint src/ test/",
"lint:typescript": "resin-lint --typescript src/ test/ typings/ && tsc --noEmit"
"lint:coffee": "balena-lint src/ test/",
"lint:typescript": "balena-lint -e ts -e js --typescript src/ test/ typings/ && tsc --noEmit"
},
"private": true,
"dependencies": {
@ -38,6 +37,7 @@
},
"devDependencies": {
"@balena/contrato": "^0.2.1",
"@balena/lint": "^4.1.0",
"@types/bluebird": "^3.5.30",
"@types/chai": "^4.2.10",
"@types/chai-as-promised": "^7.1.2",
@ -104,7 +104,6 @@
"pinejs-client-request": "^5.2.0",
"pretty-ms": "^5.1.0",
"request": "^2.88.2",
"resin-lint": "^3.2.4",
"resin-register-device": "^3.0.0",
"resumable-request": "^2.0.0",
"rimraf": "^2.6.2",

View File

@ -7,130 +7,150 @@
// a few dropColumn and dropTable calls to delete things that were removed throughout the supervisor's
// history without actually adding drop statements (mostly just becoming unused, but still there).
exports.up = function (knex, Promise) {
const addColumn = function (table, column, type) {
return knex.schema.hasColumn(table, column)
.then((exists) => {
if (!exists) {
return knex.schema.table(table, (t) => { return t[type](column) })
}
})
}
const dropColumn = function (table, column) {
return knex.schema.hasColumn(table, column)
.then((exists) => {
if (exists) {
return knex.schema.table(table, (t) => { return t.dropColumn(column) })
}
})
}
const createTableOrRun = function (tableName, tableCreator, runIfTableExists) {
return knex.schema.hasTable(tableName)
.then((exists) => {
if (!exists) {
return knex.schema.createTable(tableName, tableCreator)
} else if (runIfTableExists != null) {
return runIfTableExists()
}
})
}
const dropTable = function (tableName) {
return knex.schema.hasTable(tableName)
.then((exists) => {
if (exists) {
return knex.schema.dropTable(tableName)
}
})
}
exports.up = function(knex, Promise) {
const addColumn = function(table, column, type) {
return knex.schema.hasColumn(table, column).then(exists => {
if (!exists) {
return knex.schema.table(table, t => {
return t[type](column);
});
}
});
};
const dropColumn = function(table, column) {
return knex.schema.hasColumn(table, column).then(exists => {
if (exists) {
return knex.schema.table(table, t => {
return t.dropColumn(column);
});
}
});
};
const createTableOrRun = function(tableName, tableCreator, runIfTableExists) {
return knex.schema.hasTable(tableName).then(exists => {
if (!exists) {
return knex.schema.createTable(tableName, tableCreator);
} else if (runIfTableExists != null) {
return runIfTableExists();
}
});
};
const dropTable = function(tableName) {
return knex.schema.hasTable(tableName).then(exists => {
if (exists) {
return knex.schema.dropTable(tableName);
}
});
};
return Promise.all([
createTableOrRun('config', (t) => {
t.string('key').primary()
t.string('value')
createTableOrRun('config', t => {
t.string('key').primary();
t.string('value');
}),
createTableOrRun('deviceConfig', (t) => {
t.json('values')
t.json('targetValues')
createTableOrRun('deviceConfig', t => {
t.json('values');
t.json('targetValues');
}).then(() => {
return knex('deviceConfig').select()
.then((deviceConfigs) => {
if (deviceConfigs.length == 0) {
return knex('deviceConfig').insert({ values: '{}', targetValues: '{}' })
return knex('deviceConfig')
.select()
.then(deviceConfigs => {
if (deviceConfigs.length === 0) {
return knex('deviceConfig').insert({
values: '{}',
targetValues: '{}',
});
}
})
}),
createTableOrRun('app', (t) => {
t.increments('id').primary()
t.string('name')
t.string('containerName')
t.string('commit')
t.string('imageId')
t.string('appId')
t.boolean('privileged')
t.json('env')
t.json('config')
t.boolean('markedForDeletion')
}, () => {
return Promise.all([
addColumn('app', 'commit', 'string'),
addColumn('app', 'appId', 'string'),
addColumn('app', 'containerName', 'string'),
addColumn('app', 'config', 'json'),
addColumn('app', 'markedForDeletion', 'boolean'),
dropColumn('app', 'containerId')
]).then(() => {
//When updating from older supervisors, config can be null
return knex('app').update({ config: '{}' }).whereNull('config')
.then(() => {
knex('app').update({ markedForDeletion: false }).whereNull('markedForDeletion')
})
})
}),
createTableOrRun('dependentApp', (t) => {
t.increments('id').primary()
t.string('appId')
t.string('parentAppId')
t.string('name')
t.string('commit')
t.string('imageId')
t.json('config')
t.json('environment')
}, () => {
return addColumn('dependentApp', 'environment', 'json')
}),
createTableOrRun('dependentDevice', (t) => {
t.increments('id').primary()
t.string('uuid')
t.string('appId')
t.string('localId')
t.string('device_type')
t.string('logs_channel')
t.string('deviceId')
t.boolean('is_online')
t.string('name')
t.string('status')
t.string('download_progress')
t.string('is_managed_by')
t.dateTime('lock_expiry_date')
t.string('commit')
t.string('targetCommit')
t.json('environment')
t.json('targetEnvironment')
t.json('config')
t.json('targetConfig')
t.boolean('markedForDeletion')
}, () => {
return Promise.all([
addColumn('dependentDevice', 'markedForDeletion', 'boolean'),
addColumn('dependentDevice', 'localId', 'string'),
addColumn('dependentDevice', 'is_managed_by', 'string'),
addColumn('dependentDevice', 'lock_expiry_date', 'dateTime')
])
});
}),
createTableOrRun(
'app',
t => {
t.increments('id').primary();
t.string('name');
t.string('containerName');
t.string('commit');
t.string('imageId');
t.string('appId');
t.boolean('privileged');
t.json('env');
t.json('config');
t.boolean('markedForDeletion');
},
() => {
return Promise.all([
addColumn('app', 'commit', 'string'),
addColumn('app', 'appId', 'string'),
addColumn('app', 'containerName', 'string'),
addColumn('app', 'config', 'json'),
addColumn('app', 'markedForDeletion', 'boolean'),
dropColumn('app', 'containerId'),
]).then(() => {
//When updating from older supervisors, config can be null
return knex('app')
.update({ config: '{}' })
.whereNull('config')
.then(() => {
knex('app')
.update({ markedForDeletion: false })
.whereNull('markedForDeletion');
});
});
},
),
createTableOrRun(
'dependentApp',
t => {
t.increments('id').primary();
t.string('appId');
t.string('parentAppId');
t.string('name');
t.string('commit');
t.string('imageId');
t.json('config');
t.json('environment');
},
() => {
return addColumn('dependentApp', 'environment', 'json');
},
),
createTableOrRun(
'dependentDevice',
t => {
t.increments('id').primary();
t.string('uuid');
t.string('appId');
t.string('localId');
t.string('device_type');
t.string('logs_channel');
t.string('deviceId');
t.boolean('is_online');
t.string('name');
t.string('status');
t.string('download_progress');
t.string('is_managed_by');
t.dateTime('lock_expiry_date');
t.string('commit');
t.string('targetCommit');
t.json('environment');
t.json('targetEnvironment');
t.json('config');
t.json('targetConfig');
t.boolean('markedForDeletion');
},
() => {
return Promise.all([
addColumn('dependentDevice', 'markedForDeletion', 'boolean'),
addColumn('dependentDevice', 'localId', 'string'),
addColumn('dependentDevice', 'is_managed_by', 'string'),
addColumn('dependentDevice', 'lock_expiry_date', 'dateTime'),
]);
},
),
dropTable('image'),
dropTable('container')
])
}
dropTable('container'),
]);
};
exports.down = function(knex, Promise) {
return Promise.reject(new Error('Not implemented'))
}
return Promise.reject(new Error('Not implemented'));
};

View File

@ -1,24 +1,23 @@
const _ = require('lodash')
const _ = require('lodash');
var tryParse = function (obj) {
var tryParse = function(obj) {
try {
return JSON.parse(obj)
return JSON.parse(obj);
} catch (e) {
return {};
}
catch(e) {
return {}
}
}
};
var singleToMulticontainerApp = function (app) {
var singleToMulticontainerApp = function(app) {
// From *very* old supervisors, env or config may be null
// so we ignore errors parsing them
const conf = tryParse(app.config)
const env = tryParse(app.env)
const environment = {}
const appId = parseInt(app.appId)
const conf = tryParse(app.config);
const env = tryParse(app.env);
const environment = {};
const appId = parseInt(app.appId, 10);
for (let key in env) {
if (!/^RESIN_/.test(key)) {
environment[key] = env[key]
environment[key] = env[key];
}
}
const newApp = {
@ -27,13 +26,17 @@ var singleToMulticontainerApp = function (app) {
name: app.name,
releaseId: 1,
networks: {},
volumes: {}
}
const defaultVolume = 'resin-data'
newApp.volumes[defaultVolume] = {}
const updateStrategy = _.get(conf, 'RESIN_SUPERVISOR_UPDATE_STRATEGY', 'download-then-kill')
const handoverTimeout = _.get(conf, 'RESIN_SUPERVISOR_HANDOVER_TIMEOUT', '')
const restartPolicy = _.get(conf, 'RESIN_APP_RESTART_POLICY', 'always')
volumes: {},
};
const defaultVolume = 'resin-data';
newApp.volumes[defaultVolume] = {};
const updateStrategy = _.get(
conf,
'RESIN_SUPERVISOR_UPDATE_STRATEGY',
'download-then-kill',
);
const handoverTimeout = _.get(conf, 'RESIN_SUPERVISOR_HANDOVER_TIMEOUT', '');
const restartPolicy = _.get(conf, 'RESIN_APP_RESTART_POLICY', 'always');
newApp.services = [
{
serviceId: 1,
@ -45,9 +48,7 @@ var singleToMulticontainerApp = function (app) {
image: app.imageId,
privileged: true,
networkMode: 'host',
volumes: [
`${defaultVolume}:/data`
],
volumes: [`${defaultVolume}:/data`],
labels: {
'io.resin.features.kernel-modules': '1',
'io.resin.features.firmware': '1',
@ -56,26 +57,26 @@ var singleToMulticontainerApp = function (app) {
'io.resin.features.resin-api': '1',
'io.resin.update.strategy': updateStrategy,
'io.resin.update.handover-timeout': handoverTimeout,
'io.resin.legacy-container': '1'
'io.resin.legacy-container': '1',
},
environment: environment,
restart: restartPolicy,
running: true
}
]
return newApp
}
running: true,
},
];
return newApp;
};
var jsonifyAppFields = function (app) {
const newApp = _.clone(app)
newApp.services = JSON.stringify(app.services)
newApp.networks = JSON.stringify(app.networks)
newApp.volumes = JSON.stringify(app.volumes)
return newApp
}
var jsonifyAppFields = function(app) {
const newApp = _.clone(app);
newApp.services = JSON.stringify(app.services);
newApp.networks = JSON.stringify(app.networks);
newApp.volumes = JSON.stringify(app.volumes);
return newApp;
};
var imageForApp = function (app) {
const service = app.services[0]
var imageForApp = function(app) {
const service = app.services[0];
return {
name: service.image,
appId: service.appId,
@ -83,11 +84,11 @@ var imageForApp = function (app) {
serviceName: service.serviceName,
imageId: service.imageId,
releaseId: service.releaseId,
dependent: 0
}
}
dependent: 0,
};
};
var imageForDependentApp = function (app) {
var imageForDependentApp = function(app) {
return {
name: app.image,
appId: app.appId,
@ -95,203 +96,225 @@ var imageForDependentApp = function (app) {
serviceName: null,
imageId: app.imageId,
releaseId: null,
dependent: 1
}
}
dependent: 1,
};
};
// TODO: this whole thing is WIP
exports.up = function (knex, Promise) {
return knex.schema.createTable('image', (t) => {
t.increments('id').primary()
t.string('name')
t.integer('appId')
t.integer('serviceId')
t.string('serviceName')
t.integer('imageId')
t.integer('releaseId')
t.boolean('dependent')
exports.up = function(knex, Promise) {
return knex.schema
.createTable('image', t => {
t.increments('id').primary();
t.string('name');
t.integer('appId');
t.integer('serviceId');
t.string('serviceName');
t.integer('imageId');
t.integer('releaseId');
t.boolean('dependent');
})
.then(() => knex('app').select().whereNot({ markedForDeletion: true }).orWhereNull('markedForDeletion'))
.tap((apps) => {
.then(() =>
knex('app')
.select()
.whereNot({ markedForDeletion: true })
.orWhereNull('markedForDeletion'),
)
.tap(apps => {
if (apps.length > 0) {
return knex('config').insert({ key: 'legacyAppsPresent', value: 'true' })
return knex('config').insert({
key: 'legacyAppsPresent',
value: 'true',
});
}
})
.tap(() => {
// We're in a transaction, and it's easier to drop and recreate
// than to migrate each field...
return knex.schema.dropTable('app')
.then(() => {
return knex.schema.createTable('app', (t) => {
t.increments('id').primary()
t.string('name')
t.integer('releaseId')
t.string('commit')
t.integer('appId')
t.json('services')
t.json('networks')
t.json('volumes')
})
})
return knex.schema.dropTable('app').then(() => {
return knex.schema.createTable('app', t => {
t.increments('id').primary();
t.string('name');
t.integer('releaseId');
t.string('commit');
t.integer('appId');
t.json('services');
t.json('networks');
t.json('volumes');
});
});
})
.map((app) => {
const migratedApp = singleToMulticontainerApp(app)
return knex('app').insert(jsonifyAppFields(migratedApp))
.then(() => knex('image').insert(imageForApp(migratedApp)))
.map(app => {
const migratedApp = singleToMulticontainerApp(app);
return knex('app')
.insert(jsonifyAppFields(migratedApp))
.then(() => knex('image').insert(imageForApp(migratedApp)));
})
.then(() => {
// For some reason dropping a column in this table doesn't work. Anyways, we don't want to store old targetValues.
// Instead, on first run the supervisor will store current device config values as targets - so we want to
// make the old values that refer to supervisor config be the *current* values, and we do that by inserting
// to the config table.
return knex('deviceConfig').select()
.then((deviceConf) => {
return knex.schema.dropTable('deviceConfig')
.then(() => {
const values = JSON.parse(deviceConf[0].values)
const configKeys = {
'RESIN_SUPERVISOR_POLL_INTERVAL': 'appUpdatePollInterval',
'RESIN_SUPERVISOR_LOCAL_MODE': 'localMode',
'RESIN_SUPERVISOR_CONNECTIVITY_CHECK': 'connectivityCheckEnabled',
'RESIN_SUPERVISOR_LOG_CONTROL': 'loggingEnabled',
'RESIN_SUPERVISOR_DELTA': 'delta',
'RESIN_SUPERVISOR_DELTA_REQUEST_TIMEOUT': 'deltaRequestTimeout',
'RESIN_SUPERVISOR_DELTA_APPLY_TIMEOUT': 'deltaApplyTimeout',
'RESIN_SUPERVISOR_DELTA_RETRY_COUNT': 'deltaRetryCount',
'RESIN_SUPERVISOR_DELTA_RETRY_INTERVAL': 'deltaRequestTimeout',
'RESIN_SUPERVISOR_OVERRIDE_LOCK': 'lockOverride'
return knex('deviceConfig')
.select()
.then(deviceConf => {
return knex.schema.dropTable('deviceConfig').then(() => {
const values = JSON.parse(deviceConf[0].values);
const configKeys = {
RESIN_SUPERVISOR_POLL_INTERVAL: 'appUpdatePollInterval',
RESIN_SUPERVISOR_LOCAL_MODE: 'localMode',
RESIN_SUPERVISOR_CONNECTIVITY_CHECK: 'connectivityCheckEnabled',
RESIN_SUPERVISOR_LOG_CONTROL: 'loggingEnabled',
RESIN_SUPERVISOR_DELTA: 'delta',
RESIN_SUPERVISOR_DELTA_REQUEST_TIMEOUT: 'deltaRequestTimeout',
RESIN_SUPERVISOR_DELTA_APPLY_TIMEOUT: 'deltaApplyTimeout',
RESIN_SUPERVISOR_DELTA_RETRY_COUNT: 'deltaRetryCount',
RESIN_SUPERVISOR_DELTA_RETRY_INTERVAL: 'deltaRequestTimeout',
RESIN_SUPERVISOR_OVERRIDE_LOCK: 'lockOverride',
};
return Promise.map(Object.keys(values), envVarName => {
if (configKeys[envVarName] != null) {
return knex('config').insert({
key: configKeys[envVarName],
value: values[envVarName],
});
}
return Promise.map(Object.keys(values), (envVarName) => {
if (configKeys[envVarName] != null) {
return knex('config').insert({ key: configKeys[envVarName], value: values[envVarName]})
}
})
})
});
});
})
.then(() => {
return knex.schema.createTable('deviceConfig', (t) => {
t.json('targetValues')
})
return knex.schema.createTable('deviceConfig', t => {
t.json('targetValues');
});
})
.then(() => knex('deviceConfig').insert({ targetValues: '{}' }))
.then(() => knex('deviceConfig').insert({ targetValues: '{}' }));
})
.then(() => knex('dependentApp').select())
.then((dependentApps) => {
return knex.schema.dropTable('dependentApp')
.then(dependentApps => {
return knex.schema
.dropTable('dependentApp')
.then(() => {
return knex.schema.createTable('dependentApp', (t) => {
t.increments('id').primary()
t.integer('appId')
t.integer('parentApp')
t.string('name')
t.string('commit')
t.integer('releaseId')
t.integer('imageId')
t.string('image')
t.json('environment')
t.json('config')
})
return knex.schema.createTable('dependentApp', t => {
t.increments('id').primary();
t.integer('appId');
t.integer('parentApp');
t.string('name');
t.string('commit');
t.integer('releaseId');
t.integer('imageId');
t.string('image');
t.json('environment');
t.json('config');
});
})
.then(() => {
return knex.schema.createTable('dependentAppTarget', (t) => {
t.increments('id').primary()
t.integer('appId')
t.integer('parentApp')
t.string('name')
t.string('commit')
t.integer('releaseId')
t.integer('imageId')
t.string('image')
t.json('environment')
t.json('config')
})
return knex.schema.createTable('dependentAppTarget', t => {
t.increments('id').primary();
t.integer('appId');
t.integer('parentApp');
t.string('name');
t.string('commit');
t.integer('releaseId');
t.integer('imageId');
t.string('image');
t.json('environment');
t.json('config');
});
})
.then(() => {
return Promise.map(dependentApps, (app) => {
return Promise.map(dependentApps, app => {
const newApp = {
appId: parseInt(app.appId),
parentApp: parseInt(app.parentAppId),
appId: parseInt(app.appId, 10),
parentApp: parseInt(app.parentAppId, 10),
image: app.imageId,
releaseId: null,
commit: app.commit,
name: app.name,
config: JSON.stringify(tryParse(app.config)),
environment: JSON.stringify(tryParse(app.environment))
}
const image = imageForDependentApp(newApp)
return knex('image').insert(image)
environment: JSON.stringify(tryParse(app.environment)),
};
const image = imageForDependentApp(newApp);
return knex('image')
.insert(image)
.then(() => knex('dependentApp').insert(newApp))
.then(() => knex('dependentAppTarget').insert(newApp))
})
})
.then(() => knex('dependentAppTarget').insert(newApp));
});
});
})
.then(() => knex('dependentDevice').select())
.then((dependentDevices) => {
return knex.schema.dropTable('dependentDevice')
.then(dependentDevices => {
return knex.schema
.dropTable('dependentDevice')
.then(() => {
return knex.schema.createTable('dependentDevice', (t) => {
t.increments('id').primary()
t.string('uuid')
t.integer('appId')
t.string('localId')
t.string('device_type')
t.string('logs_channel')
t.integer('deviceId')
t.boolean('is_online')
t.string('name')
t.string('status')
t.string('download_progress')
t.integer('is_managed_by')
t.dateTime('lock_expiry_date')
t.string('commit')
t.string('targetCommit')
t.json('environment')
t.json('targetEnvironment')
t.json('config')
t.json('targetConfig')
t.boolean('markedForDeletion')
})
return knex.schema.createTable('dependentDevice', t => {
t.increments('id').primary();
t.string('uuid');
t.integer('appId');
t.string('localId');
t.string('device_type');
t.string('logs_channel');
t.integer('deviceId');
t.boolean('is_online');
t.string('name');
t.string('status');
t.string('download_progress');
t.integer('is_managed_by');
t.dateTime('lock_expiry_date');
t.string('commit');
t.string('targetCommit');
t.json('environment');
t.json('targetEnvironment');
t.json('config');
t.json('targetConfig');
t.boolean('markedForDeletion');
});
})
.then(() => {
return knex.schema.createTable('dependentDeviceTarget', (t) => {
t.increments('id').primary()
t.string('uuid')
t.string('name')
t.json('apps')
})
return knex.schema.createTable('dependentDeviceTarget', t => {
t.increments('id').primary();
t.string('uuid');
t.string('name');
t.json('apps');
});
})
.then(() => {
return Promise.map(dependentDevices, (device) => {
const newDevice = _.clone(device)
newDevice.appId = parseInt(device.appId)
newDevice.deviceId = parseInt(device.deviceId)
return Promise.map(dependentDevices, device => {
const newDevice = _.clone(device);
newDevice.appId = parseInt(device.appId, 10);
newDevice.deviceId = parseInt(device.deviceId, 10);
if (device.is_managed_by != null) {
newDevice.is_managed_by = parseInt(device.is_managed_by)
newDevice.is_managed_by = parseInt(device.is_managed_by, 10);
}
newDevice.config = JSON.stringify(tryParse(device.config))
newDevice.environment = JSON.stringify(tryParse(device.environment))
newDevice.targetConfig = JSON.stringify(tryParse(device.targetConfig))
newDevice.targetEnvironment = JSON.stringify(tryParse(device.targetEnvironment))
newDevice.config = JSON.stringify(tryParse(device.config));
newDevice.environment = JSON.stringify(
tryParse(device.environment),
);
newDevice.targetConfig = JSON.stringify(
tryParse(device.targetConfig),
);
newDevice.targetEnvironment = JSON.stringify(
tryParse(device.targetEnvironment),
);
if (newDevice.markedForDeletion == null) {
newDevice.markedForDeletion = false
newDevice.markedForDeletion = false;
}
const deviceTarget = {
uuid: device.uuid,
name: device.name,
apps: {}
}
apps: {},
};
deviceTarget.apps[device.appId] = {
commit: newDevice.targetCommit,
config: newDevice.targetConfig,
environment: newDevice.targetEnvironment
}
return knex('dependentDevice').insert(newDevice)
.then(() => knex('dependentDeviceTarget').insert(deviceTarget))
})
})
})
}
environment: newDevice.targetEnvironment,
};
return knex('dependentDevice')
.insert(newDevice)
.then(() => knex('dependentDeviceTarget').insert(deviceTarget));
});
});
});
};
exports.down = function(knex, Promise) {
return Promise.reject(new Error('Not implemented'))
}
return Promise.reject(new Error('Not implemented'));
};

View File

@ -1,10 +1,10 @@
// Adds a dockerImageId column to the image table to identify images downloaded with deltas
exports.up = function (knex, Promise) {
return knex.schema.table('image', (t) => {
t.string('dockerImageId')
})
}
exports.up = function(knex, Promise) {
return knex.schema.table('image', t => {
t.string('dockerImageId');
});
};
exports.down = function(knex, Promise) {
return Promise.reject(new Error('Not implemented'))
}
return Promise.reject(new Error('Not implemented'));
};

View File

@ -1,39 +1,45 @@
const fs = require('fs');
const configJsonPath = process.env.CONFIG_MOUNT_POINT;
exports.up = function (knex, Promise) {
exports.up = function(knex, Promise) {
return new Promise((resolve, reject) => {
if (!configJsonPath) {
console.log('Unable to locate config.json! Things may fail unexpectedly!');
console.log(
'Unable to locate config.json! Things may fail unexpectedly!',
);
resolve({});
}
fs.readFile(configJsonPath, (err, data) => {
if (err) {
console.log('Failed to read config.json! Things may fail unexpectedly!');
console.log(
'Failed to read config.json! Things may fail unexpectedly!',
);
resolve({});
return;
}
try {
const parsed = JSON.parse(data.toString());
resolve(parsed);
} catch(e) {
console.log('Failed to parse config.json! Things may fail unexpectedly!');
} catch (e) {
console.log(
'Failed to parse config.json! Things may fail unexpectedly!',
);
resolve({});
}
});
})
.then((config) => {
return knex.schema.table('app', (t) => {
}).then(config => {
return knex.schema
.table('app', t => {
// Create a new column on the table and add the apiEndpoint config json
// field if it exists
t.string('source');
})
.then(() => {
return knex('app').update({ source: (config.apiEndpoint || '') });
});
});
}
.then(() => {
return knex('app').update({ source: config.apiEndpoint || '' });
});
});
};
exports.down = function (knex, Promise) {
return Promise.reject(new Error('Not Implemented'))
}
exports.down = function(knex, Promise) {
return Promise.reject(new Error('Not Implemented'));
};

View File

@ -1,16 +1,20 @@
const fs = require('fs');
const configJsonPath = process.env.CONFIG_MOUNT_POINT;
exports.up = function (knex, Promise) {
exports.up = function(knex, Promise) {
return new Promise((resolve, reject) => {
if (!configJsonPath) {
console.log('Unable to locate config.json! Things may fail unexpectedly!');
console.log(
'Unable to locate config.json! Things may fail unexpectedly!',
);
resolve({});
return;
}
fs.readFile(configJsonPath, (err, data) => {
if (err) {
console.log('Failed to read config.json! Things may fail unexpectedly!');
console.log(
'Failed to read config.json! Things may fail unexpectedly!',
);
resolve({});
return;
}
@ -18,7 +22,9 @@ exports.up = function (knex, Promise) {
const parsed = JSON.parse(data.toString());
resolve(parsed);
} catch (e) {
console.log('Failed to parse config.json! Things may fail unexpectedly!');
console.log(
'Failed to parse config.json! Things may fail unexpectedly!',
);
resolve({});
}
});
@ -26,18 +32,20 @@ exports.up = function (knex, Promise) {
.tap(() => {
// take the logsChannelSecret, and the apiEndpoint config field,
// and store them in a new table
return knex.schema.hasTable('logsChannelSecret').then((exists) => {
return knex.schema.hasTable('logsChannelSecret').then(exists => {
if (!exists) {
return knex.schema.createTable('logsChannelSecret', (t) => {
return knex.schema.createTable('logsChannelSecret', t => {
t.string('backend');
t.string('secret');
});
}
});
})
.then((config) => {
return knex('config').where({ key: 'logsChannelSecret' }).select('value')
.then((results) => {
.then(config => {
return knex('config')
.where({ key: 'logsChannelSecret' })
.select('value')
.then(results => {
if (results.length === 0) {
return { config, secret: null };
}
@ -47,15 +55,16 @@ exports.up = function (knex, Promise) {
.then(({ config, secret }) => {
return knex('logsChannelSecret').insert({
backend: config.apiEndpoint || '',
secret
secret,
});
})
.then(() => {
return knex('config').where('key', 'logsChannelSecret').del();
return knex('config')
.where('key', 'logsChannelSecret')
.del();
});
};
}
exports.down = function (knex, Promise) {
exports.down = function(knex, Promise) {
return Promise.reject(new Error('Not Implemented'));
}
};

View File

@ -1,11 +1,10 @@
exports.up = (knex, Promise) => {
return knex.schema.createTable('engineSnapshot', (t) => {
return knex.schema.createTable('engineSnapshot', t => {
t.string('snapshot'); // Engine snapshot encoded as JSON.
t.string('timestamp'); // When the snapshot was created.
});
}
};
exports.down = (knex, Promise) => {
return Promise.reject(new Error('Not Implemented'));
}
};

View File

@ -3,18 +3,21 @@ const _ = require('lodash');
// We take legacy deviceConfig targets and store them without the RESIN_ prefix
// (we also strip the BALENA_ prefix for completeness, even though no supervisors
// using this prefix made it to production)
exports.up = function (knex, Promise) {
return knex('deviceConfig').select('targetValues')
.then((devConfigs) => {
exports.up = function(knex, Promise) {
return knex('deviceConfig')
.select('targetValues')
.then(devConfigs => {
const devConfig = devConfigs[0];
const targetValues = JSON.parse(devConfig.targetValues);
const filteredTargetValues = _.mapKeys(targetValues, (_v, k) => {
return k.replace(/^(?:RESIN|BALENA)_(.*)/, '$1');
});
return knex('deviceConfig').update({ targetValues: JSON.stringify(filteredTargetValues) });
return knex('deviceConfig').update({
targetValues: JSON.stringify(filteredTargetValues),
});
});
};
exports.down = function (knex, Promise) {
exports.down = function(knex, Promise) {
return Promise.reject(new Error('Not Implemented'));
};

View File

@ -3,9 +3,11 @@ const configJsonPath = process.env.CONFIG_MOUNT_POINT;
const { checkTruthy } = require('../lib/validation');
exports.up = function (knex, Promise) {
return knex('config').where({ key: 'localMode' }).select('value')
.then((results) => {
exports.up = function(knex, Promise) {
return knex('config')
.where({ key: 'localMode' })
.select('value')
.then(results => {
if (results.length === 0) {
// We don't need to do anything
return;
@ -14,15 +16,19 @@ exports.up = function (knex, Promise) {
let value = checkTruthy(results[0].value);
value = value != null ? value : false;
return new Promise((resolve) => {
return new Promise(resolve => {
if (!configJsonPath) {
console.log('Unable to locate config.json! Things may fail unexpectedly!');
console.log(
'Unable to locate config.json! Things may fail unexpectedly!',
);
resolve();
return;
}
fs.readFile(configJsonPath, (err, data) => {
if (err) {
console.log('Failed to read config.json! Things may fail unexpectedly!');
console.log(
'Failed to read config.json! Things may fail unexpectedly!',
);
resolve();
return;
}
@ -31,27 +37,30 @@ exports.up = function (knex, Promise) {
// Assign the local mode value
parsed.localMode = value;
fs.writeFile(configJsonPath, JSON.stringify(parsed), (err) => {
if (err) {
console.log('Failed to write config.json! Things may fail unexpectedly!');
fs.writeFile(configJsonPath, JSON.stringify(parsed), err2 => {
if (err2) {
console.log(
'Failed to write config.json! Things may fail unexpectedly!',
);
return;
}
resolve();
});
} catch (e) {
console.log('Failed to parse config.json! Things may fail unexpectedly!');
console.log(
'Failed to parse config.json! Things may fail unexpectedly!',
);
resolve();
}
});
})
.then(() => {
return knex('config').where('key', 'localMode').del();
});
}).then(() => {
return knex('config')
.where('key', 'localMode')
.del();
});
});
};
exports.down = function (knex, Promise) {
exports.down = function(knex, Promise) {
return Promise.reject(new Error('Not Implemented'));
}
};

View File

@ -3,16 +3,20 @@ const configJsonPath = process.env.CONFIG_MOUNT_POINT;
const { checkTruthy } = require('../lib/validation');
exports.up = function (knex, Promise) {
exports.up = function(knex, Promise) {
return new Promise(resolve => {
if (!configJsonPath) {
console.log('Unable to locate config.json! Things may fail unexpectedly!');
console.log(
'Unable to locate config.json! Things may fail unexpectedly!',
);
return resolve(false);
}
fs.readFile(configJsonPath, (err, data) => {
if (err) {
console.log('Failed to read config.json! Things may fail unexpectedly!');
console.log(
'Failed to read config.json! Things may fail unexpectedly!',
);
return resolve();
}
try {
@ -22,7 +26,9 @@ exports.up = function (knex, Promise) {
}
return resolve(false);
} catch (e) {
console.log('Failed to parse config.json! Things may fail unexpectedly!');
console.log(
'Failed to parse config.json! Things may fail unexpectedly!',
);
return resolve(false);
}
});
@ -36,6 +42,6 @@ exports.up = function (knex, Promise) {
});
};
exports.down = function (knex, Promise) {
exports.down = function(knex, Promise) {
return Promise.reject(new Error('Not Implemented'));
};

@ -1 +1 @@
Subproject commit 5b1459007cea18b5891d6bdf4b0694241487cc8b
Subproject commit e413fcedb9e3a5365da91c01a45e633d620947af

View File

@ -8,7 +8,8 @@
"outDir": "./build/",
"skipLibCheck": true,
"lib": ["es6"],
"resolveJsonModule": true
"resolveJsonModule": true,
"allowJs": true
},
"include": ["src/**/*.ts", "test/**/*.ts", "typings/**/*.d.ts"]
"include": ["src/**/*.ts", "src/**/*.js", "test/**/*.ts", "typings/**/*.d.ts"]
}

View File

@ -1,3 +1,3 @@
{
"extends": ["resin-lint/config/tslint-prettier.json"]
"extends": ["@balena/lint/config/tslint-prettier.json"]
}

View File

@ -2,7 +2,6 @@ var webpack = require('webpack');
var path = require('path');
var fs = require('fs');
var _ = require('lodash');
var path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
var ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
@ -102,7 +101,7 @@ module.exports = function(env) {
use: require.resolve('coffee-loader'),
},
{
test: /\.ts$/,
test: /\.ts$|\.js$/,
use: [
{
loader: 'ts-loader',
@ -122,7 +121,7 @@ module.exports = function(env) {
(m instanceof RegExp && m.test(request))
) {
return callback(null, 'commonjs ' + request);
} else if (typeof m != 'string' && !(m instanceof RegExp)) {
} else if (typeof m !== 'string' && !(m instanceof RegExp)) {
throw new Error('Invalid entry in external modules: ' + m);
}
}
@ -134,13 +133,13 @@ module.exports = function(env) {
}),
new CopyWebpackPlugin([
{
from: './src/migrations',
from: './build/migrations',
to: 'migrations',
},
]),
new webpack.ContextReplacementPlugin(
/\.\/migrations/,
path.resolve(__dirname, 'src/migrations')
path.resolve(__dirname, 'build/migrations')
),
],
};