From eac6bb5e5cef1cc3f1053415d1be6856ce89045b Mon Sep 17 00:00:00 2001 From: Pagan Gazzard Date: Thu, 27 Feb 2020 14:55:30 +0000 Subject: [PATCH] Simplify lazy-loading of balena-sdk by utilizing a shared function This also avoids instantiating multiple balena-sdk Change-type: patch --- lib/actions-oclif/devices/supported.ts | 4 ++-- lib/actions-oclif/env/add.ts | 3 ++- lib/actions-oclif/env/rename.ts | 4 ++-- lib/actions-oclif/env/rm.ts | 3 ++- lib/actions-oclif/envs.ts | 6 +++-- lib/actions-oclif/os/configure.ts | 3 ++- lib/actions/api-key.ts | 7 +++--- lib/actions/app.ts | 18 +++++++-------- lib/actions/auth.ts | 8 +++---- lib/actions/build.coffee | 3 ++- lib/actions/config.coffee | 3 ++- lib/actions/deploy.coffee | 5 ++-- lib/actions/device.coffee | 29 ++++++++++++----------- lib/actions/device_ts.ts | 4 ++-- lib/actions/join.ts | 4 ++-- lib/actions/keys.ts | 15 ++++-------- lib/actions/leave.ts | 4 ++-- lib/actions/logs.ts | 3 ++- lib/actions/notes.ts | 3 ++- lib/actions/os.coffee | 5 ++-- lib/actions/preload.coffee | 9 ++++---- lib/actions/push.ts | 3 ++- lib/actions/settings.ts | 6 ++--- lib/actions/ssh.ts | 3 ++- lib/actions/tags.ts | 7 +++--- lib/actions/tunnel.ts | 4 ++-- lib/auth/index.ts | 4 +++- lib/auth/server.ts | 6 ++--- lib/auth/utils.ts | 9 ++++---- lib/events.ts | 3 +-- lib/utils/compose.coffee | 3 ++- lib/utils/config.ts | 19 ++++++++------- lib/utils/helpers.ts | 10 ++++---- lib/utils/lazy.ts | 32 ++++++++++++++++++++++++++ lib/utils/patterns.ts | 5 ++-- lib/utils/qemu.coffee | 3 ++- tests/auth/utils.spec.ts | 3 ++- 37 files changed, 152 insertions(+), 111 deletions(-) create mode 100644 lib/utils/lazy.ts diff --git a/lib/actions-oclif/devices/supported.ts b/lib/actions-oclif/devices/supported.ts index 687f199d..a08250ba 100644 --- a/lib/actions-oclif/devices/supported.ts +++ b/lib/actions-oclif/devices/supported.ts @@ -20,6 +20,7 @@ import { stripIndent } from 'common-tags'; import * as _ from 'lodash'; import * as cf from '../../utils/common-flags'; +import { getBalenaSdk } from '../../utils/lazy'; import { CommandHelp } from '../../utils/oclif-utils'; interface FlagsDef { @@ -75,10 +76,9 @@ export default class DevicesSupportedCmd extends Command { public async run() { const { flags: options } = this.parse(DevicesSupportedCmd); - const sdk = SDK.fromSharedOptions(); let deviceTypes: Array> = await sdk.models.config.getDeviceTypes(); + >> = await getBalenaSdk().models.config.getDeviceTypes(); if (!options.discontinued) { deviceTypes = deviceTypes.filter(dt => dt.state !== 'DISCONTINUED'); } diff --git a/lib/actions-oclif/env/add.ts b/lib/actions-oclif/env/add.ts index c42b2f11..9f1b56da 100644 --- a/lib/actions-oclif/env/add.ts +++ b/lib/actions-oclif/env/add.ts @@ -22,6 +22,7 @@ import * as _ from 'lodash'; import { ExpectedError } from '../../errors'; import * as cf from '../../utils/common-flags'; +import { getBalenaSdk } from '../../utils/lazy'; import { CommandHelp } from '../../utils/oclif-utils'; interface FlagsDef { @@ -103,7 +104,6 @@ export default class EnvAddCmd extends Command { EnvAddCmd, ); const cmd = this; - const balena = (await import('balena-sdk')).fromSharedOptions(); const { checkLoggedIn } = await import('../../utils/patterns'); if (!options.application && !options.device) { @@ -128,6 +128,7 @@ export default class EnvAddCmd extends Command { } } + const balena = getBalenaSdk(); const reservedPrefixes = await getReservedPrefixes(balena); const isConfigVar = _.some(reservedPrefixes, prefix => _.startsWith(params.name, prefix), diff --git a/lib/actions-oclif/env/rename.ts b/lib/actions-oclif/env/rename.ts index 5cecdc4a..e94d6ba1 100644 --- a/lib/actions-oclif/env/rename.ts +++ b/lib/actions-oclif/env/rename.ts @@ -19,6 +19,7 @@ import { stripIndent } from 'common-tags'; import * as cf from '../../utils/common-flags'; import * as ec from '../../utils/env-common'; +import { getBalenaSdk } from '../../utils/lazy'; import { CommandHelp } from '../../utils/oclif-utils'; type IArg = import('@oclif/parser').args.IArg; @@ -83,12 +84,11 @@ export default class EnvRenameCmd extends Command { const { args: params, flags: opt } = this.parse( EnvRenameCmd, ); - const balena = (await import('balena-sdk')).fromSharedOptions(); const { checkLoggedIn } = await import('../../utils/patterns'); await checkLoggedIn(); - await balena.pine.patch({ + await getBalenaSdk().pine.patch({ resource: ec.getVarResourceName(opt.config, opt.device, opt.service), id: params.id, body: { diff --git a/lib/actions-oclif/env/rm.ts b/lib/actions-oclif/env/rm.ts index 4c76f9d4..2bcc6c94 100644 --- a/lib/actions-oclif/env/rm.ts +++ b/lib/actions-oclif/env/rm.ts @@ -19,6 +19,7 @@ import { Command, flags } from '@oclif/command'; import { stripIndent } from 'common-tags'; import * as ec from '../../utils/env-common'; +import { getBalenaSdk } from '../../utils/lazy'; import { CommandHelp } from '../../utils/oclif-utils'; type IArg = import('@oclif/parser').args.IArg; @@ -85,7 +86,7 @@ export default class EnvRmCmd extends Command { const { args: params, flags: opt } = this.parse( EnvRmCmd, ); - const balena = (await import('balena-sdk')).fromSharedOptions(); + const balena = getBalenaSdk(); const { checkLoggedIn, confirm } = await import('../../utils/patterns'); await checkLoggedIn(); diff --git a/lib/actions-oclif/envs.ts b/lib/actions-oclif/envs.ts index 7983a593..23b53678 100644 --- a/lib/actions-oclif/envs.ts +++ b/lib/actions-oclif/envs.ts @@ -21,6 +21,7 @@ import * as _ from 'lodash'; import { ExpectedError } from '../errors'; import * as cf from '../utils/common-flags'; +import { getBalenaSdk } from '../utils/lazy'; import { CommandHelp } from '../utils/oclif-utils'; interface FlagsDef { @@ -131,8 +132,6 @@ export default class EnvsCmd extends Command { public async run() { const { flags: options } = this.parse(EnvsCmd); - const balena = SDK.fromSharedOptions(); - const { getDeviceAndMaybeAppFromUUID } = await import('../utils/cloud'); const { checkLoggedIn } = await import('../utils/patterns'); const variables: EnvironmentVariableInfo[] = []; @@ -142,6 +141,9 @@ export default class EnvsCmd extends Command { throw new ExpectedError('You must specify an application or device'); } + const balena = getBalenaSdk(); + const { getDeviceAndMaybeAppFromUUID } = await import('../utils/cloud'); + let appName = options.application; let fullUUID: string | undefined; // as oppposed to the short, 7-char UUID diff --git a/lib/actions-oclif/os/configure.ts b/lib/actions-oclif/os/configure.ts index b2aefe15..07c1cd10 100644 --- a/lib/actions-oclif/os/configure.ts +++ b/lib/actions-oclif/os/configure.ts @@ -24,6 +24,7 @@ import * as path from 'path'; import { ExpectedError } from '../../errors'; import * as cf from '../../utils/common-flags'; +import { getBalenaSdk } from '../../utils/lazy'; import { CommandHelp } from '../../utils/oclif-utils'; const BOOT_PARTITION = 1; @@ -177,7 +178,6 @@ export default class OsConfigureCmd extends Command { await validateOptions(options); const devInit = await import('balena-device-init'); - const balena = (await import('balena-sdk')).fromSharedOptions(); const fs = await import('mz/fs'); const { generateDeviceConfig, generateApplicationConfig } = await import( '../../utils/config' @@ -188,6 +188,7 @@ export default class OsConfigureCmd extends Command { let device: BalenaSdk.Device | undefined; let deviceTypeSlug: string; + const balena = getBalenaSdk(); if (options.device) { device = await balena.models['device'].get(options.device); deviceTypeSlug = device.device_type; diff --git a/lib/actions/api-key.ts b/lib/actions/api-key.ts index 3b1df2fc..672563f2 100644 --- a/lib/actions/api-key.ts +++ b/lib/actions/api-key.ts @@ -1,5 +1,6 @@ import { CommandDefinition } from 'capitano'; import { stripIndent } from 'common-tags'; +import { getBalenaSdk } from '../utils/lazy'; export const generate: CommandDefinition<{ name: string; @@ -18,10 +19,8 @@ export const generate: CommandDefinition<{ $ balena api-key generate "Jenkins Key" `, async action(params, _options, done) { - const balena = (await import('balena-sdk')).fromSharedOptions(); - - balena.models.apiKey - .create(params.name) + getBalenaSdk() + .models.apiKey.create(params.name) .then(key => { console.log(stripIndent` Registered api key '${params.name}': diff --git a/lib/actions/app.ts b/lib/actions/app.ts index 09052634..9bb8ebe1 100644 --- a/lib/actions/app.ts +++ b/lib/actions/app.ts @@ -16,6 +16,7 @@ limitations under the License. import { Application } from 'balena-sdk'; import { CommandDefinition } from 'capitano'; +import { getBalenaSdk } from '../utils/lazy'; import * as commandOptions from './command-options'; export const create: CommandDefinition< @@ -54,7 +55,7 @@ Examples: ], permission: 'user', async action(params, options, done) { - const balena = (await import('balena-sdk')).fromSharedOptions(); + const balena = getBalenaSdk(); const patterns = await import('../utils/patterns'); @@ -103,7 +104,7 @@ Examples: primary: true, async action(_params, _options, done) { const _ = await import('lodash'); - const balena = (await import('balena-sdk')).fromSharedOptions(); + const balena = getBalenaSdk(); const visuals = await import('resin-cli-visuals'); return balena.models.application @@ -154,11 +155,10 @@ Examples: permission: 'user', primary: true, async action(params, _options, done) { - const balena = (await import('balena-sdk')).fromSharedOptions(); const visuals = await import('resin-cli-visuals'); - return balena.models.application - .get(params.name) + return getBalenaSdk() + .models.application.get(params.name) .then(application => { console.log( visuals.table.vertical(application, [ @@ -188,8 +188,9 @@ Examples: `, permission: 'user', async action(params, _options, done) { - const balena = (await import('balena-sdk')).fromSharedOptions(); - return balena.models.application.restart(params.name).nodeify(done); + return getBalenaSdk() + .models.application.restart(params.name) + .nodeify(done); }, }; @@ -213,7 +214,6 @@ Examples: options: [commandOptions.yes], permission: 'user', async action(params, options, done) { - const balena = (await import('balena-sdk')).fromSharedOptions(); const patterns = await import('../utils/patterns'); return patterns @@ -221,7 +221,7 @@ Examples: options.yes ?? false, 'Are you sure you want to delete the application?', ) - .then(() => balena.models.application.remove(params.name)) + .then(() => getBalenaSdk().models.application.remove(params.name)) .nodeify(done); }, }; diff --git a/lib/actions/auth.ts b/lib/actions/auth.ts index bafe49db..e0828630 100644 --- a/lib/actions/auth.ts +++ b/lib/actions/auth.ts @@ -15,6 +15,7 @@ limitations under the License. */ import { CommandDefinition } from 'capitano'; +import { getBalenaSdk } from '../utils/lazy'; export const login: CommandDefinition< {}, @@ -83,7 +84,7 @@ Examples: primary: true, async action(_params, options) { type Options = typeof options; - const balena = (await import('balena-sdk')).fromSharedOptions(); + const balena = getBalenaSdk(); const patterns = await import('../utils/patterns'); const messages = await import('../utils/messages'); @@ -155,8 +156,7 @@ Examples: $ balena logout\ `, async action(_params) { - const balena = (await import('balena-sdk')).fromSharedOptions(); - await balena.auth.logout(); + await getBalenaSdk().auth.logout(); }, }; @@ -172,7 +172,7 @@ Examples: `, permission: 'user', async action() { - const balena = (await import('balena-sdk')).fromSharedOptions(); + const balena = getBalenaSdk(); const [username, email, url] = await Promise.all([ balena.auth.whoami(), diff --git a/lib/actions/build.coffee b/lib/actions/build.coffee index b5591e10..a235af6e 100644 --- a/lib/actions/build.coffee +++ b/lib/actions/build.coffee @@ -21,6 +21,7 @@ Promise = require('bluebird') dockerUtils = require('../utils/docker') compose = require('../utils/compose') { registrySecretsHelp } = require('../utils/messages') +{ getBalenaSdk } = require('../utils/lazy') ### Opts must be an object with the following keys: @@ -118,7 +119,7 @@ module.exports = # compositions with many services trigger misleading warnings require('events').defaultMaxListeners = 1000 - sdk = (require('balena-sdk')).fromSharedOptions() + sdk = getBalenaSdk() { ExpectedError } = require('../errors') { validateProjectDirectory } = require('../utils/compose_ts') helpers = require('../utils/helpers') diff --git a/lib/actions/config.coffee b/lib/actions/config.coffee index 935f774c..c09eda0d 100644 --- a/lib/actions/config.coffee +++ b/lib/actions/config.coffee @@ -16,6 +16,7 @@ limitations under the License. commandOptions = require('./command-options') { normalizeUuidProp } = require('../utils/normalization') +{ getBalenaSdk } = require('../utils/lazy') exports.read = signature: 'config read' @@ -288,7 +289,7 @@ exports.generate = normalizeUuidProp(options, 'device') Promise = require('bluebird') writeFileAsync = Promise.promisify(require('fs').writeFile) - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() form = require('resin-cli-form') prettyjson = require('prettyjson') diff --git a/lib/actions/deploy.coffee b/lib/actions/deploy.coffee index c782bce9..45411849 100644 --- a/lib/actions/deploy.coffee +++ b/lib/actions/deploy.coffee @@ -22,6 +22,7 @@ dockerUtils = require('../utils/docker') compose = require('../utils/compose') { registrySecretsHelp } = require('../utils/messages') { ExpectedError } = require('../errors') +{ getBalenaSdk } = require('../utils/lazy') ### Opts must be an object with the following keys: @@ -37,7 +38,7 @@ Opts must be an object with the following keys: deployProject = (docker, logger, composeOpts, opts) -> _ = require('lodash') doodles = require('resin-doodles') - sdk = require('balena-sdk').fromSharedOptions() + sdk = getBalenaSdk() { loadProject } = require('../utils/compose_ts') Promise.resolve(loadProject(logger, composeOpts, opts.image)) @@ -196,7 +197,7 @@ module.exports = action: (params, options, done) -> # compositions with many services trigger misleading warnings require('events').defaultMaxListeners = 1000 - sdk = (require('balena-sdk')).fromSharedOptions() + sdk = getBalenaSdk() { ExpectedError } = require('../errors') { validateProjectDirectory } = require('../utils/compose_ts') helpers = require('../utils/helpers') diff --git a/lib/actions/device.coffee b/lib/actions/device.coffee index 6ed21821..9ff7bba8 100644 --- a/lib/actions/device.coffee +++ b/lib/actions/device.coffee @@ -17,6 +17,7 @@ limitations under the License. commandOptions = require('./command-options') _ = require('lodash') { normalizeUuidProp } = require('../utils/normalization') +{ getBalenaSdk } = require('../utils/lazy') expandForAppName = { $expand: belongs_to__application: $select: 'app_name' @@ -42,7 +43,7 @@ exports.list = primary: true action: (params, options, done) -> Promise = require('bluebird') - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() visuals = require('resin-cli-visuals') Promise.try -> @@ -86,7 +87,7 @@ exports.info = primary: true action: (params, options, done) -> normalizeUuidProp(params) - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() visuals = require('resin-cli-visuals') balena.models.device.get(params.uuid, expandForAppName) @@ -139,7 +140,7 @@ exports.register = ] action: (params, options, done) -> Promise = require('bluebird') - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() Promise.join( balena.models.application.get(params.application) @@ -169,7 +170,7 @@ exports.remove = permission: 'user' action: (params, options, done) -> normalizeUuidProp(params) - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() patterns = require('../utils/patterns') patterns.confirm(options.yes, 'Are you sure you want to delete the device?').then -> @@ -191,7 +192,7 @@ exports.identify = permission: 'user' action: (params, options, done) -> normalizeUuidProp(params) - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() balena.models.device.identify(params.uuid).nodeify(done) exports.reboot = @@ -208,7 +209,7 @@ exports.reboot = permission: 'user' action: (params, options, done) -> normalizeUuidProp(params) - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() balena.models.device.reboot(params.uuid, options).nodeify(done) exports.shutdown = @@ -225,7 +226,7 @@ exports.shutdown = permission: 'user' action: (params, options, done) -> normalizeUuidProp(params) - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() balena.models.device.shutdown(params.uuid, options).nodeify(done) exports.enableDeviceUrl = @@ -241,7 +242,7 @@ exports.enableDeviceUrl = permission: 'user' action: (params, options, done) -> normalizeUuidProp(params) - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() balena.models.device.enableDeviceUrl(params.uuid).nodeify(done) exports.disableDeviceUrl = @@ -257,7 +258,7 @@ exports.disableDeviceUrl = permission: 'user' action: (params, options, done) -> normalizeUuidProp(params) - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() balena.models.device.disableDeviceUrl(params.uuid).nodeify(done) exports.getDeviceUrl = @@ -273,7 +274,7 @@ exports.getDeviceUrl = permission: 'user' action: (params, options, done) -> normalizeUuidProp(params) - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() balena.models.device.getDeviceUrl(params.uuid).then (url) -> console.log(url) .nodeify(done) @@ -291,7 +292,7 @@ exports.hasDeviceUrl = permission: 'user' action: (params, options, done) -> normalizeUuidProp(params) - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() balena.models.device.hasDeviceUrl(params.uuid).then (hasDeviceUrl) -> console.log(hasDeviceUrl) .nodeify(done) @@ -313,7 +314,7 @@ exports.rename = action: (params, options, done) -> normalizeUuidProp(params) Promise = require('bluebird') - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() form = require('resin-cli-form') Promise.try -> @@ -343,7 +344,7 @@ exports.move = options: [ commandOptions.optionalApplication ] action: (params, options, done) -> normalizeUuidProp(params) - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() patterns = require('../utils/patterns') balena.models.device.get(params.uuid, expandForAppName).then (device) -> @@ -405,7 +406,7 @@ exports.init = tmpNameAsync = Promise.promisify(tmp.tmpName) tmp.setGracefulCleanup() - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() patterns = require('../utils/patterns') { runCommand } = require('../utils/helpers') diff --git a/lib/actions/device_ts.ts b/lib/actions/device_ts.ts index 2aa67a76..6b53fdad 100644 --- a/lib/actions/device_ts.ts +++ b/lib/actions/device_ts.ts @@ -16,6 +16,7 @@ limitations under the License. import { Device } from 'balena-sdk'; import { CommandDefinition } from 'capitano'; import { stripIndent } from 'common-tags'; +import { getBalenaSdk } from '../utils/lazy'; import { normalizeUuidProp } from '../utils/normalization'; import * as commandOptions from './command-options'; @@ -47,9 +48,8 @@ export const osUpdate: CommandDefinition = { permission: 'user', async action(params, options, done) { normalizeUuidProp(params); - const balena = await import('balena-sdk'); const _ = await import('lodash'); - const sdk = balena.fromSharedOptions(); + const sdk = getBalenaSdk(); const patterns = await import('../utils/patterns'); const form = await import('resin-cli-form'); diff --git a/lib/actions/join.ts b/lib/actions/join.ts index e9539018..dc31de8d 100644 --- a/lib/actions/join.ts +++ b/lib/actions/join.ts @@ -16,6 +16,7 @@ limitations under the License. import * as Bluebird from 'bluebird'; import { CommandDefinition } from 'capitano'; import { stripIndent } from 'common-tags'; +import { getBalenaSdk } from '../utils/lazy'; interface Args { deviceIp?: string; @@ -64,10 +65,9 @@ export const join: CommandDefinition = { primary: true, async action(params, options, done) { - const balena = await import('balena-sdk'); const Logger = await import('../utils/logger'); const promote = await import('../utils/promote'); - const sdk = balena.fromSharedOptions(); + const sdk = getBalenaSdk(); const logger = Logger.getLogger(); return Bluebird.try(() => { return promote.join(logger, sdk, params.deviceIp, options.application); diff --git a/lib/actions/keys.ts b/lib/actions/keys.ts index 6d5bf6a2..e70f9e7c 100644 --- a/lib/actions/keys.ts +++ b/lib/actions/keys.ts @@ -15,6 +15,7 @@ limitations under the License. */ import { CommandDefinition } from 'capitano'; +import { getBalenaSdk } from '../utils/lazy'; import * as commandOptions from './command-options'; export const list: CommandDefinition = { @@ -29,9 +30,7 @@ Examples: `, permission: 'user', async action() { - const balena = (await import('balena-sdk')).fromSharedOptions(); - - const keys = await balena.models.key.getAll(); + const keys = await getBalenaSdk().models.key.getAll(); const visuals = await import('resin-cli-visuals'); console.log(visuals.table.horizontal(keys, ['id', 'title'])); @@ -50,9 +49,7 @@ Examples: `, permission: 'user', async action(params) { - const balena = (await import('balena-sdk')).fromSharedOptions(); - - const key = await balena.models.key.get(params.id); + const key = await getBalenaSdk().models.key.get(params.id); const visuals = await import('resin-cli-visuals'); console.log(visuals.table.vertical(key, ['id', 'title'])); @@ -91,8 +88,7 @@ Examples: 'Are you sure you want to delete the key?', ); - const balena = (await import('balena-sdk')).fromSharedOptions(); - await balena.models.key.remove(params.id); + await getBalenaSdk().models.key.remove(params.id); }, }; @@ -122,7 +118,6 @@ Examples: key = await getStdin(); } - const balena = (await import('balena-sdk')).fromSharedOptions(); - await balena.models.key.create(params.name, key); + await getBalenaSdk().models.key.create(params.name, key); }, }; diff --git a/lib/actions/leave.ts b/lib/actions/leave.ts index aa21651c..9928bb6b 100644 --- a/lib/actions/leave.ts +++ b/lib/actions/leave.ts @@ -16,6 +16,7 @@ limitations under the License. import * as Bluebird from 'bluebird'; import { CommandDefinition } from 'capitano'; import { stripIndent } from 'common-tags'; +import { getBalenaSdk } from '../utils/lazy'; interface Args { deviceIp?: string; @@ -47,10 +48,9 @@ export const leave: CommandDefinition = { primary: true, async action(params, _options, done) { - const balena = await import('balena-sdk'); const Logger = await import('../utils/logger'); const promote = await import('../utils/promote'); - const sdk = balena.fromSharedOptions(); + const sdk = getBalenaSdk(); const logger = Logger.getLogger(); return Bluebird.try(() => { return promote.leave(logger, sdk, params.deviceIp); diff --git a/lib/actions/logs.ts b/lib/actions/logs.ts index 39e7ebd1..1d41165d 100644 --- a/lib/actions/logs.ts +++ b/lib/actions/logs.ts @@ -18,6 +18,7 @@ import { LogMessage } from 'balena-sdk'; import { CommandDefinition } from 'capitano'; import { stripIndent } from 'common-tags'; +import { getBalenaSdk } from '../utils/lazy'; import { normalizeUuidProp } from '../utils/normalization'; import { validateDotLocalUrl } from '../utils/validation'; @@ -84,7 +85,7 @@ export const logs: CommandDefinition< primary: true, async action(params, options, done) { normalizeUuidProp(params); - const balena = (await import('balena-sdk')).fromSharedOptions(); + const balena = getBalenaSdk(); const isArray = await import('lodash/isArray'); const { serviceIdToName } = await import('../utils/cloud'); const { displayDeviceLogs, displayLogObject } = await import( diff --git a/lib/actions/notes.ts b/lib/actions/notes.ts index 2e62d57d..ff2b80e8 100644 --- a/lib/actions/notes.ts +++ b/lib/actions/notes.ts @@ -15,6 +15,7 @@ limitations under the License. */ import { CommandDefinition } from 'capitano'; +import { getBalenaSdk } from '../utils/lazy'; export const set: CommandDefinition<{ note: string }, { device: string }> = { signature: 'note <|note>', @@ -45,7 +46,7 @@ Examples: const { normalizeUuidProp } = await import('../utils/normalization'); normalizeUuidProp(options, 'device'); const _ = await import('lodash'); - const balena = (await import('balena-sdk')).fromSharedOptions(); + const balena = getBalenaSdk(); if (_.isEmpty(params.note)) { const { exitWithExpectedError } = await import('../utils/patterns'); diff --git a/lib/actions/os.coffee b/lib/actions/os.coffee index 12d835f4..9375fa3e 100644 --- a/lib/actions/os.coffee +++ b/lib/actions/os.coffee @@ -16,6 +16,7 @@ limitations under the License. commandOptions = require('./command-options') _ = require('lodash') +{ getBalenaSdk } = require('../utils/lazy') formatVersion = (v, isRecommended) -> result = "v#{v}" @@ -30,7 +31,7 @@ resolveVersion = (deviceType, version) -> return Promise.resolve(version) form = require('resin-cli-form') - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() balena.models.os.getSupportedVersions(deviceType) .then ({ versions, recommended }) -> @@ -56,7 +57,7 @@ exports.versions = $ balena os versions raspberrypi3 ''' action: (params, options, done) -> - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() balena.models.os.getSupportedVersions(params.type) .then ({ versions, recommended }) -> diff --git a/lib/actions/preload.coffee b/lib/actions/preload.coffee index 3572ed85..3a9d00f6 100644 --- a/lib/actions/preload.coffee +++ b/lib/actions/preload.coffee @@ -15,6 +15,7 @@ limitations under the License. ### _ = require('lodash') +{ getBalenaSdk } = require('../utils/lazy') dockerUtils = require('../utils/docker') @@ -28,7 +29,7 @@ getDeviceTypes = -> _ = require('lodash') if allDeviceTypes != undefined return Bluebird.resolve(allDeviceTypes) - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() balena.models.config.getDeviceTypes() .then (deviceTypes) -> _.sortBy(deviceTypes, 'name') @@ -45,7 +46,7 @@ getDeviceTypesWithSameArch = (deviceTypeSlug) -> getApplicationsWithSuccessfulBuilds = (deviceType) -> preload = require('balena-preload') - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() getDeviceTypesWithSameArch(deviceType) .then (deviceTypes) -> @@ -103,7 +104,7 @@ selectApplicationCommit = (releases) -> offerToDisableAutomaticUpdates = (application, commit, pinDevice) -> Promise = require('bluebird') - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() form = require('resin-cli-form') if isCurrent(commit) or not application.should_track_latest_release or pinDevice @@ -206,7 +207,7 @@ module.exports = action: (params, options, done) -> _ = require('lodash') Promise = require('bluebird') - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() preload = require('balena-preload') visuals = require('resin-cli-visuals') nodeCleanup = require('node-cleanup') diff --git a/lib/actions/push.ts b/lib/actions/push.ts index 8307cabe..2aa36f2a 100644 --- a/lib/actions/push.ts +++ b/lib/actions/push.ts @@ -19,6 +19,7 @@ import { CommandDefinition } from 'capitano'; import { stripIndent } from 'common-tags'; import { ExpectedError } from '../errors'; +import { getBalenaSdk } from '../utils/lazy'; import { registrySecretsHelp } from '../utils/messages'; import { validateApplicationName, @@ -260,7 +261,7 @@ export const push: CommandDefinition< }, ], async action(params, options) { - const sdk = (await import('balena-sdk')).fromSharedOptions(); + const sdk = getBalenaSdk(); const Bluebird = await import('bluebird'); const isArray = await import('lodash/isArray'); const remote = await import('../utils/remote-build'); diff --git a/lib/actions/settings.ts b/lib/actions/settings.ts index ba391ea8..e276712d 100644 --- a/lib/actions/settings.ts +++ b/lib/actions/settings.ts @@ -15,6 +15,7 @@ limitations under the License. */ import { CommandDefinition } from 'capitano'; +import { getBalenaSdk } from '../utils/lazy'; export const list: CommandDefinition = { signature: 'settings', @@ -27,11 +28,10 @@ Examples: $ balena settings\ `, async action(_params, _options, done) { - const balena = (await import('balena-sdk')).fromSharedOptions(); const prettyjson = await import('prettyjson'); - return balena.settings - .getAll() + return getBalenaSdk() + .settings.getAll() .then(prettyjson.render) .then(console.log) .nodeify(done); diff --git a/lib/actions/ssh.ts b/lib/actions/ssh.ts index c4bf5dde..84afe981 100644 --- a/lib/actions/ssh.ts +++ b/lib/actions/ssh.ts @@ -18,6 +18,7 @@ import { CommandDefinition } from 'capitano'; import { stripIndent } from 'common-tags'; import { BalenaDeviceNotFound } from 'balena-errors'; +import { getBalenaSdk } from '../utils/lazy'; import { validateDotLocalUrl, validateIPAddress } from '../utils/validation'; async function getContainerId( @@ -236,7 +237,7 @@ export const ssh: CommandDefinition< exitWithExpectedError, getOnlineTargetUuid, } = await import('../utils/patterns'); - const sdk = BalenaSdk.fromSharedOptions(); + const sdk = getBalenaSdk(); const verbose = options.verbose === true; const proxyConfig = getProxyConfig(); diff --git a/lib/actions/tags.ts b/lib/actions/tags.ts index 2f984a86..02b27c3a 100644 --- a/lib/actions/tags.ts +++ b/lib/actions/tags.ts @@ -17,6 +17,7 @@ limitations under the License. import { ApplicationTag, DeviceTag, ReleaseTag } from 'balena-sdk'; import { CommandDefinition } from 'capitano'; import { stripIndent } from 'common-tags'; +import { getBalenaSdk } from '../utils/lazy'; import { disambiguateReleaseParam, normalizeUuidProp, @@ -57,7 +58,7 @@ export const list: CommandDefinition< normalizeUuidProp(options, 'device'); const Bluebird = await import('bluebird'); const _ = await import('lodash'); - const balena = (await import('balena-sdk')).fromSharedOptions(); + const balena = getBalenaSdk(); const visuals = await import('resin-cli-visuals'); const { exitWithExpectedError } = await import('../utils/patterns'); @@ -162,7 +163,7 @@ export const set: CommandDefinition< normalizeUuidProp(options, 'device'); const Bluebird = await import('bluebird'); const _ = await import('lodash'); - const balena = (await import('balena-sdk')).fromSharedOptions(); + const balena = getBalenaSdk(); const { exitWithExpectedError } = await import('../utils/patterns'); @@ -255,7 +256,7 @@ export const remove: CommandDefinition< async action(params, options, done) { const Bluebird = await import('bluebird'); const _ = await import('lodash'); - const balena = (await import('balena-sdk')).fromSharedOptions(); + const balena = getBalenaSdk(); const { exitWithExpectedError } = await import('../utils/patterns'); return Bluebird.try(async () => { diff --git a/lib/actions/tunnel.ts b/lib/actions/tunnel.ts index c936d943..9536d673 100644 --- a/lib/actions/tunnel.ts +++ b/lib/actions/tunnel.ts @@ -20,6 +20,7 @@ import * as _ from 'lodash'; import { createServer, Server, Socket } from 'net'; import { isArray } from 'util'; +import { getBalenaSdk } from '../utils/lazy'; import { getOnlineTargetUuid } from '../utils/patterns'; import { tunnelConnectionToDevice } from '../utils/tunnel'; @@ -95,8 +96,7 @@ export const tunnel: CommandDefinition = { params.deviceOrApplication_raw || params.deviceOrApplication; const Logger = await import('../utils/logger'); const logger = Logger.getLogger(); - const balena = await import('balena-sdk'); - const sdk = balena.fromSharedOptions(); + const sdk = getBalenaSdk(); const logConnection = ( fromHost: string, diff --git a/lib/auth/index.ts b/lib/auth/index.ts index f43e812b..90016d9e 100644 --- a/lib/auth/index.ts +++ b/lib/auth/index.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { getBalenaSdk } from '../utils/lazy'; + /** * @module auth */ @@ -58,6 +60,6 @@ export const login = async () => { }, 1000); const server = await import('./server'); - const balena = (await import('balena-sdk')).fromSharedOptions(); + const balena = getBalenaSdk(); return server.awaitForToken(options).tap(balena.auth.loginWithToken); }; diff --git a/lib/auth/server.ts b/lib/auth/server.ts index 411dae4a..559a2af3 100644 --- a/lib/auth/server.ts +++ b/lib/auth/server.ts @@ -14,15 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import * as balenaSdk from 'balena-sdk'; import * as Promise from 'bluebird'; import * as bodyParser from 'body-parser'; import * as express from 'express'; import * as path from 'path'; +import { getBalenaSdk } from '../utils/lazy'; import * as utils from './utils'; -const balena = balenaSdk.fromSharedOptions(); - const createServer = ({ port }: { port: number }) => { const app = express(); app.use( @@ -130,7 +128,7 @@ export const awaitForToken = (options: { export const getContext = (viewName: 'success' | 'error') => { if (viewName === 'success') { return Promise.props({ - dashboardUrl: balena.settings.get('dashboardUrl'), + dashboardUrl: getBalenaSdk().settings.get('dashboardUrl'), }); } diff --git a/lib/auth/utils.ts b/lib/auth/utils.ts index 2f52828d..674eaf31 100644 --- a/lib/auth/utils.ts +++ b/lib/auth/utils.ts @@ -14,12 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import * as balenaSdk from 'balena-sdk'; import * as Promise from 'bluebird'; import * as _ from 'lodash'; import * as url from 'url'; - -const balena = balenaSdk.fromSharedOptions(); +import { getBalenaSdk } from '../utils/lazy'; /** * @summary Get dashboard CLI login URL @@ -39,8 +37,8 @@ export const getDashboardLoginURL = (callbackUrl: string) => { // characters to avoid angular getting confused. callbackUrl = encodeURIComponent(callbackUrl).replace(/%/g, '%25'); - return balena.settings - .get('dashboardUrl') + return getBalenaSdk() + .settings.get('dashboardUrl') .then(dashboardUrl => url.resolve(dashboardUrl, `/login/cli/${callbackUrl}`), ); @@ -70,6 +68,7 @@ export const loginIfTokenValid = (token: string) => { if (_.isEmpty(token?.trim())) { return Promise.resolve(false); } + const balena = getBalenaSdk(); return balena.auth .getToken() diff --git a/lib/events.ts b/lib/events.ts index 4e76e7f1..a5bd167d 100644 --- a/lib/events.ts +++ b/lib/events.ts @@ -14,15 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import BalenaSdk = require('balena-sdk'); import Promise = require('bluebird'); import _ = require('lodash'); import Mixpanel = require('mixpanel'); import Raven = require('raven'); import packageJSON = require('../package.json'); +import { getBalenaSdk } from './utils/lazy'; -const getBalenaSdk = _.once(() => BalenaSdk.fromSharedOptions()); const getMixpanel = _.once(() => { const settings = require('balena-settings-client'); return Mixpanel.init('balena-main', { diff --git a/lib/utils/compose.coffee b/lib/utils/compose.coffee index ebd4366c..a069c51c 100644 --- a/lib/utils/compose.coffee +++ b/lib/utils/compose.coffee @@ -17,6 +17,7 @@ Promise = require('bluebird') path = require('path') +{ getBalenaSdk } = require('./lazy') exports.appendProjectOptions = appendProjectOptions = (opts) -> opts.concat [ @@ -455,7 +456,7 @@ exports.deployProject = ( tagServiceImages(docker, images, serviceImages) .tap (images) -> logger.logDebug('Authorizing push...') - sdk = require('balena-sdk').fromSharedOptions() + sdk = getBalenaSdk() getPreviousRepos(sdk, docker, logger, appId) .then (previousRepos) -> authorizePush(sdk, logger, apiEndpoint, images[0].registry, _.map(images, 'repo'), previousRepos) diff --git a/lib/utils/config.ts b/lib/utils/config.ts index d5ff3f5f..c6418613 100644 --- a/lib/utils/config.ts +++ b/lib/utils/config.ts @@ -16,8 +16,7 @@ limitations under the License. import BalenaSdk = require('balena-sdk'); import Promise = require('bluebird'); import * as semver from 'resin-semver'; - -const balena = BalenaSdk.fromSharedOptions(); +import { getBalenaSdk } from './lazy'; export interface ImgConfig { applicationName: string; @@ -68,7 +67,7 @@ export function generateBaseConfig( appUpdatePollInterval: options.appUpdatePollInterval || 10, }; - const promise = balena.models.os.getConfig( + const promise = getBalenaSdk().models.os.getConfig( application.app_name, options, ) as Promise; @@ -106,8 +105,8 @@ export function generateDeviceConfig( deviceApiKey: string | true | undefined, options: { version: string }, ) { - return balena.models.application - .get(device.belongs_to__application.__id) + return getBalenaSdk() + .models.application.get(device.belongs_to__application.__id) .then(application => { const baseConfigOpts = { ...options, @@ -135,16 +134,16 @@ export function generateDeviceConfig( } function addApplicationKey(config: any, applicationNameOrId: string | number) { - return balena.models.application - .generateApiKey(applicationNameOrId) + return getBalenaSdk() + .models.application.generateApiKey(applicationNameOrId) .tap(apiKey => { config.apiKey = apiKey; }); } function addProvisioningKey(config: any, applicationNameOrId: string | number) { - return balena.models.application - .generateProvisioningKey(applicationNameOrId) + return getBalenaSdk() + .models.application.generateProvisioningKey(applicationNameOrId) .tap(apiKey => { config.apiKey = apiKey; }); @@ -157,7 +156,7 @@ function addDeviceKey( ) { return Promise.try(() => { if (customDeviceApiKey === true) { - return balena.models.device.generateDeviceKey(uuid); + return getBalenaSdk().models.device.generateDeviceKey(uuid); } else { return customDeviceApiKey; } diff --git a/lib/utils/helpers.ts b/lib/utils/helpers.ts index 403be32d..5017ee81 100644 --- a/lib/utils/helpers.ts +++ b/lib/utils/helpers.ts @@ -15,7 +15,7 @@ limitations under the License. */ import { InitializeEmitter, OperationState } from 'balena-device-init'; -import BalenaSdk = require('balena-sdk'); +import * as BalenaSdk from 'balena-sdk'; import Bluebird = require('bluebird'); import chalk from 'chalk'; import _ = require('lodash'); @@ -24,8 +24,7 @@ import visuals = require('resin-cli-visuals'); import * as ShellEscape from 'shell-escape'; import { ExpectedError } from '../errors'; - -const balena = BalenaSdk.fromSharedOptions(); +import { getBalenaSdk } from './lazy'; export function getGroupDefaults(group: { options: Array<{ name: string; default?: string }>; @@ -109,7 +108,7 @@ export async function getManifest( if (manifest != null) { return manifest; } - return balena.models.device.getManifestBySlug(deviceType); + return getBalenaSdk().models.device.getManifestBySlug(deviceType); } export const areDeviceTypesCompatible = ( @@ -148,7 +147,7 @@ export function getArchAndDeviceType( ): Bluebird<{ arch: string; device_type: string }> { return Bluebird.join( getApplication(applicationName), - balena.models.config.getDeviceTypes(), + getBalenaSdk().models.config.getDeviceTypes(), function(app, deviceTypes) { const config = _.find(deviceTypes, { slug: app.device_type, @@ -176,6 +175,7 @@ export function getApplication(applicationName: string) { }, }; + const balena = getBalenaSdk(); if (match.length > 1) { return balena.models.application.getAppByOwner( match[1], diff --git a/lib/utils/lazy.ts b/lib/utils/lazy.ts new file mode 100644 index 00000000..c3eb67fa --- /dev/null +++ b/lib/utils/lazy.ts @@ -0,0 +1,32 @@ +/* +Copyright 2020 Balena + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import * as BalenaSdk from 'balena-sdk'; + +// Equivalent of _.once but avoiding the need to import lodash for lazy deps +const once = (fn: () => T) => { + let cached: T; + return (): T => { + if (!cached) { + cached = fn(); + } + return cached; + }; +}; + +export const getBalenaSdk = once(() => + (require('balena-sdk') as typeof BalenaSdk).fromSharedOptions(), +); diff --git a/lib/utils/patterns.ts b/lib/utils/patterns.ts index 67521cb4..19b0629b 100644 --- a/lib/utils/patterns.ts +++ b/lib/utils/patterns.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ import { BalenaApplicationNotFound } from 'balena-errors'; -import BalenaSdk = require('balena-sdk'); +import * as BalenaSdk from 'balena-sdk'; import Bluebird = require('bluebird'); import chalk from 'chalk'; import { stripIndent } from 'common-tags'; @@ -23,11 +23,10 @@ import _form = require('resin-cli-form'); import _visuals = require('resin-cli-visuals'); import { NotLoggedInError } from '../errors'; +import { getBalenaSdk } from './lazy'; import messages = require('./messages'); import validation = require('./validation'); -const getBalenaSdk = _.once(() => BalenaSdk.fromSharedOptions()); - const getForm = _.once((): typeof _form => require('resin-cli-form')); const getVisuals = _.once((): typeof _visuals => require('resin-cli-visuals')); diff --git a/lib/utils/qemu.coffee b/lib/utils/qemu.coffee index 6beecbf9..e91ffb2c 100644 --- a/lib/utils/qemu.coffee +++ b/lib/utils/qemu.coffee @@ -16,6 +16,7 @@ ### Promise = require('bluebird') +{ getBalenaSdk } = require('./lazy') exports.QEMU_VERSION = QEMU_VERSION = 'v4.0.0-balena' exports.QEMU_BIN_NAME = QEMU_BIN_NAME = 'qemu-execve' @@ -53,7 +54,7 @@ exports.copyQemu = (context, arch) -> path.relative(context, binPath) exports.getQemuPath = getQemuPath = (arch) -> - balena = require('balena-sdk').fromSharedOptions() + balena = getBalenaSdk() path = require('path') fs = require('mz/fs') diff --git a/tests/auth/utils.spec.ts b/tests/auth/utils.spec.ts index 92fa2151..6e339bb1 100644 --- a/tests/auth/utils.spec.ts +++ b/tests/auth/utils.spec.ts @@ -3,10 +3,11 @@ import { expect } from 'chai'; import rewire = require('rewire'); import * as sinon from 'sinon'; import * as url from 'url'; +import { getBalenaSdk } from '../../build/utils/lazy'; import tokens from './tokens'; const utils = rewire('../../build/auth/utils'); -const balena = utils.__get__('balena'); +const balena = getBalenaSdk(); describe('Utils:', function() { describe('.getDashboardLoginURL()', function() {