Skip to content

Commit

Permalink
feat: enabled passing env vars into podman containers (#627)
Browse files Browse the repository at this point in the history
* enabled passing [user defined] env vars into podman containers

* feat: node config can be an env var

---------

Co-authored-by: Johns Gresham <[email protected]>
  • Loading branch information
corn-potage and jgresham authored Sep 21, 2024
1 parent 217d4e8 commit a8e005e
Show file tree
Hide file tree
Showing 4 changed files with 255 additions and 4 deletions.
205 changes: 205 additions & 0 deletions src/__tests__/node/buildCliConfig.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import { describe, expect, it } from 'vitest';
import { buildCliConfig, buildEnvInput } from '../../common/nodeConfig.js';

describe('Building cli config and env variable cli input', () => {
it('Successfully creates a single env variable cli input', async () => {
const configValuesMap = { plexClaimCode: 'claim123' };
const configTranslationMap = {
plexClaimCode: {
displayName: 'Plex claim code',
cliConfigPrefix: 'PLEX_CLAIM=',
uiControl: {
type: 'text',
},
isEnvVariable: true,
addNodeFlow: 'required',
infoDescription:
'Sign in at https://www.plex.tv/claim/, get a code, and paste it here',
defaultValue: '',
documentation: 'https://www.plex.tv/claim/',
},
};
const cliConfig1 = buildCliConfig({
configValuesMap,
configTranslationMap,
excludeConfigKeys: [],
isBuildingEnvInput: true,
});

expect(cliConfig1).toEqual('-e PLEX_CLAIM=claim123');
});

it('Successfully creates multiple env variable cli input', async () => {
const configValuesMap = { plexClaimCode: 'claim123', httpPortEnv: '8080' };
const configTranslationMap = {
plexClaimCode: {
displayName: 'Plex claim code',
cliConfigPrefix: 'PLEX_CLAIM=',
uiControl: {
type: 'text',
},
isEnvVariable: true,
addNodeFlow: 'required',
infoDescription:
'Sign in at https://www.plex.tv/claim/, get a code, and paste it here',
defaultValue: '',
documentation: 'https://www.plex.tv/claim/',
},
httpPortEnv: {
displayName: 'HTTP PORT',
cliConfigPrefix: 'HTTP_PORT=',
uiControl: {
type: 'text',
},
isEnvVariable: true,
},
};
const cliConfig1 = buildCliConfig({
configValuesMap,
configTranslationMap,
excludeConfigKeys: [],
isBuildingEnvInput: true,
});

expect(cliConfig1).toEqual('-e PLEX_CLAIM=claim123 -e HTTP_PORT=8080');
});

it('Successfully creates a single cli config input', async () => {
const configValuesMap = { httpPort: '8080' };
const configTranslationMap = {
httpPort: {
displayName: 'HTTP PORT',
cliConfigPrefix: '--http-port=',
uiControl: {
type: 'text',
},
},
};
const cliConfig1 = buildCliConfig({
configValuesMap,
configTranslationMap,
excludeConfigKeys: [],
});

expect(cliConfig1).toEqual('--http-port=8080');
});

it('Successfully creates multiple cli config input', async () => {
const configValuesMap = {
httpPort: '8080',
network: 'Mainnet',
plexClaimCode: 'claimIgnoreMe',
};
const configTranslationMap = {
httpPort: {
displayName: 'HTTP PORT',
cliConfigPrefix: '--http-port=',
uiControl: {
type: 'text',
},
},
network: {
displayName: 'Network',
defaultValue: 'Mainnet',
hideFromUserAfterStart: true,
uiControl: {
type: 'select/single',
controlTranslations: [
{
value: 'Mainnet',
config: '--mainnet',
},
{
value: 'Holesky',
config: '--holesky',
},
{
value: 'Sepolia',
config: '--sepolia',
},
],
},
},
plexClaimCode: {
displayName: 'Plex claim code',
cliConfigPrefix: 'PLEX_CLAIM=',
uiControl: {
type: 'text',
},
isEnvVariable: true,
addNodeFlow: 'required',
infoDescription:
'Sign in at https://www.plex.tv/claim/, get a code, and paste it here',
defaultValue: '',
documentation: 'https://www.plex.tv/claim/',
},
};
const cliConfig1 = buildCliConfig({
configValuesMap,
configTranslationMap,
excludeConfigKeys: [],
});

expect(cliConfig1).toEqual('--http-port=8080 --mainnet');
});

it('Successfully ignores a single cli config input when building env input', async () => {
const configValuesMap = { httpPort: '8080' };
const configTranslationMap = {
httpPort: {
displayName: 'HTTP PORT',
cliConfigPrefix: '--http-port=',
uiControl: {
type: 'text',
},
},
};
const cliConfig1 = buildCliConfig({
configValuesMap,
configTranslationMap,
excludeConfigKeys: [],
isBuildingEnvInput: true,
});

expect(cliConfig1).toEqual('');
});

it('Successfully ignores a single cli config input and adds 1 env var when building env input', async () => {
const configValuesMap = { httpPort: '8080', myFavEnvVar: 'muchwow' };
const configTranslationMap = {
httpPort: {
displayName: 'HTTP PORT',
cliConfigPrefix: '--http-port=',
uiControl: {
type: 'text',
},
},
myFavEnvVar: {
displayName: 'Plex claim code',
cliConfigPrefix: 'MYFAVENVVAR=',
uiControl: {
type: 'text',
},
isEnvVariable: true,
},
};
const cliConfig1 = buildCliConfig({
configValuesMap,
configTranslationMap,
excludeConfigKeys: [],
isBuildingEnvInput: true,
});

expect(cliConfig1).toEqual('-e MYFAVENVVAR=muchwow');
});

it('[buildEnvInput] Successfully builds env input for 2 user supplied env vars', async () => {
const configValuesMap = {
envInput: 'ETHPORTPORT=123456789,BASEPORT=987',
myFavEnvVar: 'muchwow',
};
const cliConfig1 = buildEnvInput(configValuesMap.envInput);

expect(cliConfig1).toEqual('-e ETHPORTPORT=123456789 -e BASEPORT=987');
});
});
12 changes: 12 additions & 0 deletions src/common/node-spec-tool/injectDefaultControllerConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ export const injectDefaultControllerConfig = (nodeSpec: NodeSpecification) => {
nodeSpec.configTranslation = {};
}

if (!nodeSpec.configTranslation.envInput) {
nodeSpec.configTranslation.envInput = {
displayName: `${nodeSpec.displayName} ENV input`,
uiControl: {
type: 'text',
},
defaultValue: '',
addNodeFlow: 'advanced',
infoDescription: 'Additional ENV input, comma separated',
};
}

if (!nodeSpec.configTranslation.cliInput) {
nodeSpec.configTranslation.cliInput = {
displayName: `${nodeSpec.displayName} CLI input`,
Expand Down
28 changes: 25 additions & 3 deletions src/common/nodeConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export type ConfigTranslation = {
addNodeFlow?: ConfigTranslationAddNodeFlow;
initCommandConfig?: boolean;
hideFromUserAfterStart?: boolean;
isEnvVariable?: boolean;
};

export type ConfigKey = string;
Expand Down Expand Up @@ -89,23 +90,31 @@ export const buildCliConfig = ({
configValuesMap,
configTranslationMap,
excludeConfigKeys,
isBuildingEnvInput,
}: {
configValuesMap: ConfigValuesMap;
configTranslationMap?: ConfigTranslationMap;
excludeConfigKeys?: string[];
isBuildingEnvInput?: boolean;
}): string => {
if (!configTranslationMap) return '';
const cliConfigArray = Object.keys(configValuesMap).reduce(
(cliString, configKey) => {
if (excludeConfigKeys?.includes(configKey)) {
return cliString;
}
const configValue = configValuesMap[configKey];
const configTranslation: ConfigTranslation =
configTranslationMap[configKey];

// Only include env vars if building env input, ignore env vars if building cli input
if (
excludeConfigKeys?.includes(configKey) ||
isBuildingEnvInput !== configTranslation?.isEnvVariable
) {
return cliString;
}

if (configTranslation && configValue) {
let currCliString = '';

if (configTranslation.cliConfigPrefix) {
if (typeof configTranslation.cliConfigPrefix === 'string') {
currCliString = configTranslation.cliConfigPrefix;
Expand Down Expand Up @@ -161,12 +170,17 @@ export const buildCliConfig = ({
currCliString += ` ${configTranslation.cliConfigPrefix[i]}${configValue}`;
}
}
// each env var needs to be prefixed with -e for podman/docker
if (isBuildingEnvInput) {
currCliString = `-e ${currCliString}`;
}

console.log(
'cliString, currCliString: ',
JSON.stringify(cliString),
JSON.stringify(currCliString),
);

// join the current config with the previous and a space between
if (cliString) {
return `${cliString} ${currCliString}`;
Expand All @@ -179,3 +193,11 @@ export const buildCliConfig = ({
);
return cliConfigArray;
};

export const buildEnvInput = (input: string) => {
if (!input) {
return '';
}
const pairs = input.split(',');
return pairs.map((pair) => `-e ${pair.trim()}`).join(' ');
};
14 changes: 13 additions & 1 deletion src/main/podman/podman.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
type ConfigValuesMap,
buildCliConfig,
} from '../../common/nodeConfig';
import { buildEnvInput } from '../../common/nodeConfig.js';
import { CHANNELS, send } from '../messenger.js';
import { restartNodes } from '../nodePackageManager';
import { isLinux } from '../platform';
Expand Down Expand Up @@ -546,6 +547,7 @@ export const createRunCommand = (node: Node): string => {
const excludeConfigKeys = [
'dataDir',
'serviceVersion',
'envInput',
...initCommandConfigKeys,
];
if (
Expand All @@ -562,14 +564,24 @@ export const createRunCommand = (node: Node): string => {
});
nodeInput += ` ${cliConfigInput}`;

const directUserEnvConfigInput = buildEnvInput(
node.config.configValuesMap.envInput,
);
const envConfigInput = buildCliConfig({
configValuesMap: node.config.configValuesMap,
configTranslationMap: node.spec.configTranslation,
excludeConfigKeys,
isBuildingEnvInput: true,
});

const imageTag = getImageTag(node);
// if imageTage is empty, use then imageTag is already included in the imageName (backwards compatability)
const imageNameWithTag =
imageTag !== '' ? `${imageName}:${imageTag}` : imageName;

const containerName = getContainerName(node);
// -q quiets podman logs (pulling new image logs) so we can parse the containerId
const podmanCommand = `run -q -d --name ${containerName} ${finalPodmanInput} ${imageNameWithTag} ${nodeInput}`;
const podmanCommand = `run -q -d ${envConfigInput} ${directUserEnvConfigInput} --name ${containerName} ${finalPodmanInput} ${imageNameWithTag} ${nodeInput}`;
logger.info(`podman run command ${podmanCommand}`);
return podmanCommand;
};
Expand Down

0 comments on commit a8e005e

Please sign in to comment.