UNPKG

balena-cli

Version:

The official balena Command Line Interface

200 lines • 7.01 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.dockerCliFlags = exports.dockerConnectionCliFlags = void 0; exports.generateBuildOpts = generateBuildOpts; exports.isBalenaEngine = isBalenaEngine; exports.getDocker = getDocker; exports.createClient = createClient; exports.getDefaultDockerModemOpts = getDefaultDockerModemOpts; exports.generateConnectOpts = generateConnectOpts; const core_1 = require("@oclif/core"); const errors_1 = require("../errors"); const validation_1 = require("./validation"); exports.dockerConnectionCliFlags = { docker: core_1.Flags.string({ description: 'Path to a local docker socket (e.g. /var/run/docker.sock)', char: 'P', }), dockerHost: core_1.Flags.string({ description: 'Docker daemon hostname or IP address (dev machine or balena device) ', char: 'h', }), dockerPort: core_1.Flags.integer({ description: 'Docker daemon TCP port number (hint: 2375 for balena devices)', char: 'p', parse: async (p) => (0, validation_1.parseAsInteger)(p, 'dockerPort'), }), ca: core_1.Flags.string({ description: 'Docker host TLS certificate authority file', }), cert: core_1.Flags.string({ description: 'Docker host TLS certificate file', }), key: core_1.Flags.string({ description: 'Docker host TLS key file', }), }; exports.dockerCliFlags = { tag: core_1.Flags.string({ description: `\ Tag locally built Docker images. This is the 'tag' portion in 'projectName_serviceName:tag'. The default is 'latest'.`, char: 't', }), buildArg: core_1.Flags.string({ description: '[Deprecated] Set a build-time variable (eg. "-B \'ARG=value\'"). Can be specified multiple times.', char: 'B', multiple: true, }), 'cache-from': core_1.Flags.string({ description: `\ Comma-separated list (no spaces) of image names for build cache resolution. \ Implements the same feature as the "docker build --cache-from" option.`, }), nocache: core_1.Flags.boolean({ description: "Don't use docker layer caching when building", }), pull: core_1.Flags.boolean({ description: 'Pull the base images again even if they exist locally', }), squash: core_1.Flags.boolean({ description: 'Squash newly built layers into a single new layer', }), ...exports.dockerConnectionCliFlags, }; function parseBuildArgs(args) { if (!Array.isArray(args)) { args = [args]; } const buildArgs = {}; args.forEach(function (arg) { var _a; const pair = /^([^\s]+?)=([^]*)$/.exec(arg); if (pair != null) { buildArgs[pair[1]] = (_a = pair[2]) !== null && _a !== void 0 ? _a : ''; } else { throw new errors_1.ExpectedError(`Could not parse build argument: '${arg}'`); } }); return buildArgs; } function generateBuildOpts(options) { var _a; const opts = {}; if (options.buildArg != null) { opts.buildargs = parseBuildArgs(options.buildArg); } if ((_a = options['cache-from']) === null || _a === void 0 ? void 0 : _a.trim()) { opts.cachefrom = options['cache-from'].split(',').filter((i) => !!i.trim()); } if (options.nocache != null) { opts.nocache = true; } if (options.pull != null) { opts.pull = true; } if (options['registry-secrets'] && Object.keys(options['registry-secrets']).length) { opts.registryconfig = options['registry-secrets']; } if (options.squash != null) { opts.squash = true; } if (options.tag != null) { opts.t = options.tag; } return opts; } async function isBalenaEngine(docker) { const dockerVersion = (await docker.version()); return !!(dockerVersion.Engine && dockerVersion.Engine.match(/balena|balaena/)); } async function getDocker(options) { const connectOpts = await generateConnectOpts(options); const client = await createClient(connectOpts); await checkThatDockerIsReachable(client); return client; } async function createClient(opts) { const Docker = await Promise.resolve().then(() => require('dockerode')); return new Docker(opts); } function getDefaultDockerModemOpts(opts) { var _a; const connectOpts = {}; const optsOfInterest = [ 'ca', 'cert', 'key', 'host', 'port', 'socketPath', 'protocol', 'username', 'timeout', ]; const Modem = require('docker-modem'); const originalDockerHost = process.env.DOCKER_HOST; try { if (opts.dockerHost) { (_a = process.env).DOCKER_HOST || (_a.DOCKER_HOST = opts.dockerPort ? `${opts.dockerHost}:${opts.dockerPort}` : opts.dockerHost); } const defaultOpts = new Modem(); for (const opt of optsOfInterest) { connectOpts[opt] = defaultOpts[opt]; } } finally { if (originalDockerHost) { process.env.DOCKER_HOST = originalDockerHost; } else { delete process.env.DOCKER_HOST; } } return connectOpts; } async function generateConnectOpts(opts) { let connectOpts = getDefaultDockerModemOpts(opts); if (opts.docker != null && opts.dockerHost == null) { connectOpts.socketPath = opts.docker; delete connectOpts.host; delete connectOpts.port; } else if (opts.dockerHost != null && opts.docker == null) { connectOpts.host = opts.dockerHost; connectOpts.port = opts.dockerPort || 2376; delete connectOpts.socketPath; } else if (opts.docker != null && opts.dockerHost != null) { throw new errors_1.ExpectedError("Both a local docker socket and docker host have been provided. Don't know how to continue."); } const tlsOpts = [opts.ca, opts.cert, opts.key]; if (tlsOpts.some((opt) => opt)) { if (!tlsOpts.every((opt) => opt)) { throw new errors_1.ExpectedError('You must provide a CA, certificate and key in order to use TLS'); } if (!isStringArray(tlsOpts)) { throw new errors_1.ExpectedError('TLS options (CA, certificate and key) must be file paths (strings)'); } const { promises: fs } = await Promise.resolve().then(() => require('fs')); const [ca, cert, key] = await Promise.all(tlsOpts.map((opt) => fs.readFile(opt, 'utf8'))); connectOpts = { ...connectOpts, ca, cert, key, protocol: 'https' }; } return connectOpts; } function isStringArray(array) { return array.every((opt) => typeof opt === 'string'); } async function checkThatDockerIsReachable(docker) { try { await docker.ping(); } catch (e) { throw new errors_1.ExpectedError(`Docker seems to be unavailable. Is it installed and running?\n${e}`); } } //# sourceMappingURL=docker.js.map