mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-01-18 18:56:25 +00:00
Merge pull request #1934 from balena-io/bump-preload-v10
Adjustments for balena-preload v10 (SDK v14). Improved error reporting.
This commit is contained in:
commit
644e643fab
@ -1929,7 +1929,7 @@ Examples:
|
||||
|
||||
#### --app, -a <appId>
|
||||
|
||||
id of the application to preload
|
||||
Name, slug or numeric ID of the application to preload
|
||||
|
||||
#### --commit, -c <hash>
|
||||
|
||||
|
@ -20,6 +20,27 @@ import * as dockerUtils from '../utils/docker';
|
||||
|
||||
const isCurrent = (commit) => commit === 'latest' || commit === 'current';
|
||||
|
||||
/** @type {any} */
|
||||
const applicationExpandOptions = {
|
||||
owns__release: {
|
||||
$select: ['id', 'commit', 'end_timestamp', 'composition'],
|
||||
$orderby: [{ end_timestamp: 'desc' }, { id: 'desc' }],
|
||||
$expand: {
|
||||
contains__image: {
|
||||
$select: ['image'],
|
||||
$expand: {
|
||||
image: {
|
||||
$select: ['image_size', 'is_stored_at__image_location'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
$filter: {
|
||||
status: 'success',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let allDeviceTypes;
|
||||
const getDeviceTypes = function () {
|
||||
const Bluebird = require('bluebird');
|
||||
@ -38,12 +59,14 @@ const getDeviceTypes = function () {
|
||||
const getDeviceTypesWithSameArch = function (deviceTypeSlug) {
|
||||
return getDeviceTypes().then(function (deviceTypes) {
|
||||
const deviceType = _.find(deviceTypes, { slug: deviceTypeSlug });
|
||||
if (!deviceType) {
|
||||
throw new Error(`Device type "${deviceTypeSlug}" not found in API query`);
|
||||
}
|
||||
return _(deviceTypes).filter({ arch: deviceType.arch }).map('slug').value();
|
||||
});
|
||||
};
|
||||
|
||||
const getApplicationsWithSuccessfulBuilds = function (deviceType) {
|
||||
const balenaPreload = require('balena-preload');
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
return getDeviceTypesWithSameArch(deviceType).then((deviceTypes) => {
|
||||
@ -64,7 +87,7 @@ const getApplicationsWithSuccessfulBuilds = function (deviceType) {
|
||||
},
|
||||
},
|
||||
},
|
||||
$expand: balenaPreload.applicationExpandOptions,
|
||||
$expand: applicationExpandOptions,
|
||||
$select: [
|
||||
'id',
|
||||
'app_name',
|
||||
@ -148,16 +171,23 @@ const offerToDisableAutomaticUpdates = function (
|
||||
}
|
||||
const message = `\
|
||||
|
||||
This application is set to automatically update all devices to the current version.
|
||||
This might be unexpected behavior: with this enabled, the preloaded device will still
|
||||
download and install the current release once it is online.
|
||||
This application is set to track the latest release, and non-pinned devices
|
||||
are automatically updated when a new release is available. This may lead to
|
||||
unexpected behavior: The preloaded device will download and install the latest
|
||||
release once it is online.
|
||||
|
||||
Do you want to disable automatic updates for this application?
|
||||
This prompt gives you the opportunity to disable automatic updates for this
|
||||
application now. Note that this would result in the application being pinned
|
||||
to the current latest release, rather than some other release that may have
|
||||
been selected for preloading. The pinned released may be further managed
|
||||
through the web dashboard or programatically through the balena API / SDK.
|
||||
Documentation about release policies and app/device pinning can be found at:
|
||||
https://www.balena.io/docs/learn/deploy/release-strategy/release-policy/
|
||||
|
||||
Warning: To re-enable this requires direct api calls,
|
||||
see https://balena.io/docs/reference/api/resources/device/#set-device-to-release
|
||||
Alternatively, the --pin-device-to-release flag may be used to pin only the
|
||||
preloaded device to the selected release.
|
||||
|
||||
Alternatively you can pass the --pin-device-to-release flag to pin only this device to the selected release.\
|
||||
Would you like to disable automatic updates for this application now?\
|
||||
`;
|
||||
return getCliForm()
|
||||
.ask({
|
||||
@ -178,11 +208,73 @@ Alternatively you can pass the --pin-device-to-release flag to pin only this dev
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('balena-sdk').BalenaSDK} balenaSdk
|
||||
* @param {string | number} appId
|
||||
* @returns {Promise<import('balena-sdk').Application>}
|
||||
*/
|
||||
async function getAppWithReleases(balenaSdk, appId) {
|
||||
return balenaSdk.models.application.get(appId, {
|
||||
$expand: applicationExpandOptions,
|
||||
});
|
||||
}
|
||||
|
||||
async function prepareAndPreload(preloader, balenaSdk, options) {
|
||||
const { ExpectedError } = require('../errors');
|
||||
|
||||
await preloader.prepare();
|
||||
preloader.config = { deviceType: 'intel-nuc' };
|
||||
|
||||
const application = options.appId
|
||||
? await getAppWithReleases(balenaSdk, options.appId)
|
||||
: await selectApplication(preloader.config.deviceType);
|
||||
|
||||
/** @type {string} commit hash or the strings 'latest' or 'current' */
|
||||
let commit;
|
||||
|
||||
// Use the commit given as --commit or show an interactive commit selection menu
|
||||
if (options.commit) {
|
||||
if (isCurrent(options.commit)) {
|
||||
if (!application.commit) {
|
||||
throw new Error(
|
||||
`Unexpected empty commit hash for app ID "${application.id}"`,
|
||||
);
|
||||
}
|
||||
// handle `--commit current` (and its `--commit latest` synonym)
|
||||
commit = 'latest';
|
||||
} else {
|
||||
const release = _.find(application.owns__release, (r) =>
|
||||
r.commit.startsWith(options.commit),
|
||||
);
|
||||
if (!release) {
|
||||
throw new ExpectedError(
|
||||
`There is no release matching commit "${options.commit}"`,
|
||||
);
|
||||
}
|
||||
commit = release.commit;
|
||||
}
|
||||
} else {
|
||||
// this could have the value 'current'
|
||||
commit = await selectApplicationCommit(application.owns__release);
|
||||
}
|
||||
|
||||
await preloader.setAppIdAndCommit(
|
||||
application.id,
|
||||
isCurrent(commit) ? application.commit : commit,
|
||||
);
|
||||
|
||||
// Propose to disable automatic app updates if the commit is not the current release
|
||||
await offerToDisableAutomaticUpdates(application, commit, options.pinDevice);
|
||||
|
||||
// All options are ready: preload the image.
|
||||
await preloader.preload();
|
||||
}
|
||||
|
||||
const preloadOptions = dockerUtils.appendConnectionOptions([
|
||||
{
|
||||
signature: 'app',
|
||||
parameter: 'appId',
|
||||
description: 'id of the application to preload',
|
||||
description: 'Name, slug or numeric ID of the application to preload',
|
||||
alias: 'a',
|
||||
},
|
||||
{
|
||||
@ -251,12 +343,12 @@ Examples:
|
||||
permission: 'user',
|
||||
primary: true,
|
||||
options: preloadOptions,
|
||||
action(params, options, done) {
|
||||
async action(params, options) {
|
||||
const balena = getBalenaSdk();
|
||||
const balenaPreload = require('balena-preload');
|
||||
const visuals = getVisuals();
|
||||
const nodeCleanup = require('node-cleanup');
|
||||
const { exitWithExpectedError } = require('../errors');
|
||||
const { ExpectedError, instanceOf } = require('../errors');
|
||||
|
||||
const progressBars = {};
|
||||
|
||||
@ -296,7 +388,7 @@ Examples:
|
||||
options.dontCheckArch = options['dont-check-arch'] || false;
|
||||
delete options['dont-check-arch'];
|
||||
if (options.dontCheckArch && !options.appId) {
|
||||
exitWithExpectedError(
|
||||
throw new ExpectedError(
|
||||
'You need to specify an app id if you disable the architecture check.',
|
||||
);
|
||||
}
|
||||
@ -314,106 +406,62 @@ Examples:
|
||||
}
|
||||
for (let certificate of certificates) {
|
||||
if (!certificate.endsWith('.crt')) {
|
||||
exitWithExpectedError('Certificate file name must end with ".crt"');
|
||||
throw new ExpectedError('Certificate file name must end with ".crt"');
|
||||
}
|
||||
}
|
||||
|
||||
// Get a configured dockerode instance
|
||||
return dockerUtils.getDocker(options).then(function (docker) {
|
||||
const preloader = new balenaPreload.Preloader(
|
||||
balena,
|
||||
docker,
|
||||
options.appId,
|
||||
options.commit,
|
||||
options.image,
|
||||
options.splashImage,
|
||||
options.proxy,
|
||||
options.dontCheckArch,
|
||||
options.pinDevice,
|
||||
certificates,
|
||||
);
|
||||
const docker = await dockerUtils.getDocker(options);
|
||||
const preloader = new balenaPreload.Preloader(
|
||||
null,
|
||||
docker,
|
||||
options.appId,
|
||||
options.commit,
|
||||
options.image,
|
||||
options.splashImage,
|
||||
options.proxy,
|
||||
options.dontCheckArch,
|
||||
options.pinDevice,
|
||||
certificates,
|
||||
);
|
||||
|
||||
let gotSignal = false;
|
||||
let gotSignal = false;
|
||||
|
||||
nodeCleanup(function (_exitCode, signal) {
|
||||
if (signal) {
|
||||
gotSignal = true;
|
||||
nodeCleanup.uninstall(); // don't call cleanup handler again
|
||||
preloader.cleanup().then(() => {
|
||||
// calling process.exit() won't inform parent process of signal
|
||||
process.kill(process.pid, signal);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (process.env.DEBUG) {
|
||||
preloader.stderr.pipe(process.stderr);
|
||||
}
|
||||
|
||||
preloader.on('progress', progressHandler);
|
||||
preloader.on('spinner', spinnerHandler);
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
preloader.on('error', reject);
|
||||
|
||||
return preloader
|
||||
.prepare()
|
||||
.then(() => {
|
||||
// If no appId was provided, show a list of matching apps
|
||||
if (!preloader.appId) {
|
||||
return selectApplication(
|
||||
preloader.config.deviceType,
|
||||
).then((application) => preloader.setApplication(application));
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
// Use the commit given as --commit or show an interactive commit selection menu
|
||||
if (options.commit) {
|
||||
if (isCurrent(options.commit) && preloader.application.commit) {
|
||||
// handle `--commit current` (and its `--commit latest` synonym)
|
||||
return 'latest';
|
||||
}
|
||||
const release = _.find(preloader.application.owns__release, (r) =>
|
||||
r.commit.startsWith(options.commit),
|
||||
);
|
||||
if (!release) {
|
||||
exitWithExpectedError(
|
||||
'There is no release matching this commit',
|
||||
);
|
||||
}
|
||||
return release.commit;
|
||||
}
|
||||
return selectApplicationCommit(preloader.application.owns__release);
|
||||
})
|
||||
.then(function (commit) {
|
||||
if (isCurrent(commit)) {
|
||||
preloader.commit = preloader.application.commit;
|
||||
} else {
|
||||
preloader.commit = commit;
|
||||
}
|
||||
|
||||
// Propose to disable automatic app updates if the commit is not the current release
|
||||
return offerToDisableAutomaticUpdates(
|
||||
preloader.application,
|
||||
commit,
|
||||
options.pinDevice,
|
||||
);
|
||||
})
|
||||
.then(() =>
|
||||
// All options are ready: preload the image.
|
||||
preloader.preload(),
|
||||
)
|
||||
.catch(balena.errors.BalenaError, exitWithExpectedError)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
})
|
||||
.then(done)
|
||||
.finally(function () {
|
||||
if (!gotSignal) {
|
||||
return preloader.cleanup();
|
||||
}
|
||||
nodeCleanup(function (_exitCode, signal) {
|
||||
if (signal) {
|
||||
gotSignal = true;
|
||||
nodeCleanup.uninstall(); // don't call cleanup handler again
|
||||
preloader.cleanup().then(() => {
|
||||
// calling process.exit() won't inform parent process of signal
|
||||
process.kill(process.pid, signal);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (process.env.DEBUG) {
|
||||
preloader.stderr.pipe(process.stderr);
|
||||
}
|
||||
|
||||
preloader.on('progress', progressHandler);
|
||||
preloader.on('spinner', spinnerHandler);
|
||||
|
||||
try {
|
||||
await new Promise(function (resolve, reject) {
|
||||
preloader.on('error', reject);
|
||||
resolve(prepareAndPreload(preloader, balena, options));
|
||||
});
|
||||
} catch (err) {
|
||||
if (instanceOf(err, balena.errors.BalenaError)) {
|
||||
const code = err.code ? `(${err.code})` : '';
|
||||
throw new ExpectedError(`${err.message} ${code}`);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
} finally {
|
||||
if (!gotSignal) {
|
||||
await preloader.cleanup();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
74
npm-shrinkwrap.json
generated
74
npm-shrinkwrap.json
generated
@ -2447,12 +2447,12 @@
|
||||
}
|
||||
},
|
||||
"balena-preload": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balena-preload/-/balena-preload-9.0.0.tgz",
|
||||
"integrity": "sha512-K1tmZMGQHzAFHkry8mOVwTyDwsPJnn4EXYl0Sv6Ke9+B8IbdfxJzQ3uIihkBvSASME1BvbkOLX2mE+0TC2929A==",
|
||||
"version": "10.2.0",
|
||||
"resolved": "https://registry.npmjs.org/balena-preload/-/balena-preload-10.2.0.tgz",
|
||||
"integrity": "sha512-2FDL/Piy70TDEj2JRLzx7LAfyo+wUP4Y/TDFk5OKVyjqu9bDQNgzVY0COHUMS9xHKjwpCqim6Yh62GyNQ6FyDA==",
|
||||
"requires": {
|
||||
"archiver": "^3.1.1",
|
||||
"balena-sdk": "^13.8.0",
|
||||
"balena-sdk": "^14.0.0",
|
||||
"bluebird": "^3.7.2",
|
||||
"compare-versions": "^3.6.0",
|
||||
"docker-progress": "^3.0.5",
|
||||
@ -2467,6 +2467,54 @@
|
||||
"unzipper": "^0.8.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"balena-errors": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/balena-errors/-/balena-errors-4.4.1.tgz",
|
||||
"integrity": "sha512-912lPp1LyBjkpxRg6m/EpOCssqMhgkzyYbrKwtT2uRvixm89WOlJrj5sPkxnbPnp5IoMNaoRONxFt1jtiQf50Q==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.0",
|
||||
"typed-error": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"balena-sdk": {
|
||||
"version": "14.8.0",
|
||||
"resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-14.8.0.tgz",
|
||||
"integrity": "sha512-GptB/Ju8BtE1E9NODHioScdF4HW8svcDhfdMHPrDjMFWbs38KOoTgiKogDjLlvEjoAg5FIvz8DaUeH27EtZ1Rg==",
|
||||
"requires": {
|
||||
"@balena/es-version": "^1.0.0",
|
||||
"@types/bluebird": "^3.5.30",
|
||||
"@types/lodash": "^4.14.150",
|
||||
"@types/memoizee": "^0.4.3",
|
||||
"@types/node": "^10.17.20",
|
||||
"abortcontroller-polyfill": "^1.4.0",
|
||||
"balena-auth": "^3.1.0",
|
||||
"balena-errors": "^4.4.0",
|
||||
"balena-hup-action-utils": "~4.0.1",
|
||||
"balena-pine": "^11.2.0",
|
||||
"balena-register-device": "^6.1.1",
|
||||
"balena-request": "^10.0.9",
|
||||
"balena-semver": "^2.3.0",
|
||||
"balena-settings-client": "^4.0.4",
|
||||
"bluebird": "^3.7.2",
|
||||
"lodash": "^4.17.15",
|
||||
"memoizee": "^0.4.14",
|
||||
"moment": "~2.24.0 || ^2.25.1",
|
||||
"ndjson": "^1.5.0",
|
||||
"semver": "^7.3.2",
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"balena-semver": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/balena-semver/-/balena-semver-2.3.0.tgz",
|
||||
"integrity": "sha512-dlUBaYz22ZxHh3umciI/87aLcwX+HRlT1RjkpuiClO8wjkuR8U/2ZvtS16iMNe30rm2kNgAV2myamQ5eA8SxVQ==",
|
||||
"requires": {
|
||||
"@types/lodash": "^4.14.149",
|
||||
"@types/semver": "^7.1.0",
|
||||
"lodash": "^4.17.15",
|
||||
"semver": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"docker-progress": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/docker-progress/-/docker-progress-3.0.5.tgz",
|
||||
@ -2479,6 +2527,13 @@
|
||||
"lodash": "^4.0.0",
|
||||
"request": "^2.65.0",
|
||||
"semver": "^5.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"pump": {
|
||||
@ -2491,9 +2546,9 @@
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
|
||||
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ=="
|
||||
},
|
||||
"tar-fs": {
|
||||
"version": "2.1.0",
|
||||
@ -2513,6 +2568,11 @@
|
||||
"requires": {
|
||||
"os-tmpdir": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz",
|
||||
"integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -189,7 +189,7 @@
|
||||
"balena-device-init": "^5.0.2",
|
||||
"balena-errors": "^4.3.0",
|
||||
"balena-image-manager": "^7.0.1",
|
||||
"balena-preload": "^9.0.0",
|
||||
"balena-preload": "^10.2.0",
|
||||
"balena-release": "^2.1.0",
|
||||
"balena-sdk": "^13.6.0",
|
||||
"balena-semver": "^2.2.0",
|
||||
|
@ -1,20 +0,0 @@
|
||||
diff --git a/node_modules/balena-preload/src/preload.py b/node_modules/balena-preload/src/preload.py
|
||||
index 887eaf3..0c5cc7f 100755
|
||||
--- a/node_modules/balena-preload/src/preload.py
|
||||
+++ b/node_modules/balena-preload/src/preload.py
|
||||
@@ -489,8 +489,13 @@ def start_docker_daemon(storage_driver, docker_dir):
|
||||
if running_dockerd.process.exit_code is not None:
|
||||
# There is no reason for dockerd to exit with a 0 status now.
|
||||
assert running_dockerd.process.exit_code != 0
|
||||
- # This will raise an sh.ErrorReturnCode_X exception.
|
||||
- running_dockerd.wait()
|
||||
+ try:
|
||||
+ # This will raise an sh.ErrorReturnCode_X exception.
|
||||
+ running_dockerd.wait()
|
||||
+ except:
|
||||
+ print("An error has occurred executing 'dockerd':\n{}".format(
|
||||
+ running_dockerd.stderr.decode("utf8")), file=sys.stderr, flush=True)
|
||||
+ raise
|
||||
# Check that we can connect to dockerd.
|
||||
output = docker("version", _ok_code=[0, 1])
|
||||
ok = output.exit_code == 0
|
Loading…
Reference in New Issue
Block a user