mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-19 13:47:52 +00:00
Merge pull request #1256 from balena-io/1255-local-push-vars
Add the ability to specify an environment variable when pushing to local mode device
This commit is contained in:
commit
ba4301487f
@ -1478,6 +1478,7 @@ Examples:
|
||||
$ balena push 10.0.0.1
|
||||
$ balena push 10.0.0.1 --source <source directory>
|
||||
$ balena push 10.0.0.1 --service my-service
|
||||
$ balena push 10.0.0.1 --env MY_ENV_VAR=value --env my-service:SERVICE_VAR=value
|
||||
|
||||
$ balena push 23c73a1.local --system
|
||||
$ balena push 23c73a1.local --system --service my-service
|
||||
@ -1529,6 +1530,16 @@ Only valid when pushing to a local mode device.
|
||||
Only show system logs. This can be used in combination with --service.
|
||||
Only valid when pushing to a local mode device.
|
||||
|
||||
#### --env <env>
|
||||
|
||||
When performing a push to device, run the built containers with environment
|
||||
variables provided with this argument. Environment variables can be applied
|
||||
to individual services by adding their service name before the argument,
|
||||
separated by a colon, e.g:
|
||||
--env main:MY_ENV=value
|
||||
Note that if the service name cannot be found in the composition, the entire
|
||||
left hand side of the = character will be treated as the variable name.
|
||||
|
||||
# Settings
|
||||
|
||||
## settings
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2016-2018 Balena Ltd.
|
||||
Copyright 2016-2019 Balena Ltd.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -114,6 +114,7 @@ export const push: CommandDefinition<
|
||||
detached: boolean;
|
||||
service: string;
|
||||
system: boolean;
|
||||
env: string | string[];
|
||||
}
|
||||
> = {
|
||||
signature: 'push <applicationOrDevice>',
|
||||
@ -153,6 +154,7 @@ export const push: CommandDefinition<
|
||||
$ balena push 10.0.0.1
|
||||
$ balena push 10.0.0.1 --source <source directory>
|
||||
$ balena push 10.0.0.1 --service my-service
|
||||
$ balena push 10.0.0.1 --env MY_ENV_VAR=value --env my-service:SERVICE_VAR=value
|
||||
|
||||
$ balena push 23c73a1.local --system
|
||||
$ balena push 23c73a1.local --system --service my-service
|
||||
@ -224,6 +226,19 @@ export const push: CommandDefinition<
|
||||
Only valid when pushing to a local mode device.`,
|
||||
boolean: true,
|
||||
},
|
||||
{
|
||||
signature: 'env',
|
||||
parameter: 'env',
|
||||
description: stripIndent`
|
||||
When performing a push to device, run the built containers with environment
|
||||
variables provided with this argument. Environment variables can be applied
|
||||
to individual services by adding their service name before the argument,
|
||||
separated by a colon, e.g:
|
||||
--env main:MY_ENV=value
|
||||
Note that if the service name cannot be found in the composition, the entire
|
||||
left hand side of the = character will be treated as the variable name.
|
||||
`,
|
||||
},
|
||||
],
|
||||
async action(params, options, done) {
|
||||
const sdk = (await import('balena-sdk')).fromSharedOptions();
|
||||
@ -282,6 +297,11 @@ export const push: CommandDefinition<
|
||||
'The --system flag is only valid when pushing to a local mode device.',
|
||||
);
|
||||
}
|
||||
if (options.env) {
|
||||
exitWithExpectedError(
|
||||
'The --env flag is only valid when pushing to a local mode device.',
|
||||
);
|
||||
}
|
||||
|
||||
const app = appOrDevice;
|
||||
await exitIfNotLoggedIn();
|
||||
@ -324,6 +344,10 @@ export const push: CommandDefinition<
|
||||
detached: options.detached || false,
|
||||
service: options.service,
|
||||
system: options.system || false,
|
||||
env:
|
||||
typeof options.env === 'string'
|
||||
? [options.env]
|
||||
: options.env || [],
|
||||
}),
|
||||
)
|
||||
.catch(BuildError, e => {
|
||||
|
@ -50,6 +50,11 @@ export interface DeviceDeployOptions {
|
||||
detached: boolean;
|
||||
service?: string;
|
||||
system: boolean;
|
||||
env: string[];
|
||||
}
|
||||
|
||||
interface ParsedEnvironment {
|
||||
[serviceName: string]: { [key: string]: string };
|
||||
}
|
||||
|
||||
async function checkSource(source: string): Promise<boolean> {
|
||||
@ -57,6 +62,58 @@ async function checkSource(source: string): Promise<boolean> {
|
||||
return (await fs.exists(source)) && (await fs.stat(source)).isDirectory();
|
||||
}
|
||||
|
||||
async function environmentFromInput(
|
||||
envs: string[],
|
||||
serviceNames: string[],
|
||||
logger: Logger,
|
||||
): Promise<ParsedEnvironment> {
|
||||
const { exitWithExpectedError } = await import('../patterns');
|
||||
// A normal environment variable regex, with an added part
|
||||
// to find a colon followed servicename at the start
|
||||
const varRegex = /^(?:([^\s:]+):)?([^\s]+?)=(.*)$/;
|
||||
|
||||
const ret: ParsedEnvironment = {};
|
||||
// Propolulate the object with the servicenames, as it
|
||||
// also means that we can do a fast lookup of whether a
|
||||
// service exists
|
||||
for (const service of serviceNames) {
|
||||
ret[service] = {};
|
||||
}
|
||||
|
||||
for (const env of envs) {
|
||||
const maybeMatch = env.match(varRegex);
|
||||
if (maybeMatch == null) {
|
||||
exitWithExpectedError(`Unable to parse environment variable: ${env}`);
|
||||
}
|
||||
const match = maybeMatch!;
|
||||
let service: string | undefined;
|
||||
if (match[1]) {
|
||||
// This is for a service, we check that it actually
|
||||
// exists
|
||||
if (!(match[1] in ret)) {
|
||||
logger.logDebug(
|
||||
`Warning: Cannot find a service with name ${
|
||||
match[1]
|
||||
}. Treating the string as part of the environment variable name.`,
|
||||
);
|
||||
match[2] = `${match[1]}:${match[2]}`;
|
||||
} else {
|
||||
service = match[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (service != null) {
|
||||
ret[service][match[2]] = match[3];
|
||||
} else {
|
||||
for (const serviceName of serviceNames) {
|
||||
ret[serviceName][match[2]] = match[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
|
||||
const { loadProject, tarDirectory } = await import('../compose');
|
||||
const { exitWithExpectedError } = await import('../patterns');
|
||||
@ -136,6 +193,12 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
|
||||
buildLogs,
|
||||
);
|
||||
|
||||
const envs = await environmentFromInput(
|
||||
opts.env,
|
||||
Object.getOwnPropertyNames(project.composition.services),
|
||||
globalLogger,
|
||||
);
|
||||
|
||||
globalLogger.logDebug('Setting device state...');
|
||||
// Now set the target state on the device
|
||||
|
||||
@ -144,6 +207,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
|
||||
const targetState = generateTargetState(
|
||||
currentTargetState,
|
||||
project.composition,
|
||||
envs,
|
||||
);
|
||||
globalLogger.logDebug(`Sending target state: ${JSON.stringify(targetState)}`);
|
||||
|
||||
@ -376,6 +440,7 @@ function generateImageName(serviceName: string): string {
|
||||
export function generateTargetState(
|
||||
currentTargetState: any,
|
||||
composition: Composition,
|
||||
env: ParsedEnvironment,
|
||||
): any {
|
||||
const services: { [serviceId: string]: any } = {};
|
||||
let idx = 1;
|
||||
@ -390,6 +455,8 @@ export function generateTargetState(
|
||||
labels: {},
|
||||
};
|
||||
|
||||
opts.environment = _.merge(opts.environment, env[name]);
|
||||
|
||||
services[idx] = _.merge(defaults, opts, {
|
||||
imageId: idx,
|
||||
serviceName: name,
|
||||
|
@ -348,7 +348,7 @@ export class LivepushManager {
|
||||
// we rebuilt
|
||||
const comp = _.cloneDeep(this.composition);
|
||||
delete comp.services[serviceName];
|
||||
const intermediateState = generateTargetState(currentState, comp);
|
||||
const intermediateState = generateTargetState(currentState, comp, {});
|
||||
await this.api.setTargetState(intermediateState);
|
||||
|
||||
// Now we wait for the device state to settle
|
||||
@ -356,7 +356,7 @@ export class LivepushManager {
|
||||
|
||||
// And re-set the target state
|
||||
await this.api.setTargetState(
|
||||
generateTargetState(currentState, this.composition),
|
||||
generateTargetState(currentState, this.composition, {}),
|
||||
);
|
||||
|
||||
await this.awaitDeviceStateSettle();
|
||||
|
Loading…
Reference in New Issue
Block a user