-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #60 from orbs-network/feature/env-var-config-params
Support passing node configuration via environment values or config file
- Loading branch information
Showing
5 changed files
with
254 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import test from 'ava'; | ||
import { exampleConfig } from './config.example'; | ||
import { setConfigEnvVars } from './env-var-args'; | ||
import { Configuration } from './config'; | ||
|
||
test('setConfigEnvVars uses default values when no environment variables', (t) => { | ||
const input = { ...exampleConfig }; | ||
setConfigEnvVars(input); | ||
t.deepEqual(input, exampleConfig); | ||
}); | ||
|
||
/** | ||
* Converts mockEnv property names to Configuration names | ||
* Eg. ETHEREUM_ENDPOINT -> EthereumEndpoint | ||
* */ | ||
const camelCaseToSnakeCase = (str: string): string => { | ||
const result = str.replace(/([a-z])([A-Z])/g, '$1_$2'); | ||
return result.toUpperCase(); | ||
}; | ||
|
||
const mockEnv = { | ||
MANAGEMENT_SERVICE_ENDPOINT: 'http://localhost:8080', | ||
ETHEREUM_ENDPOINT: 'https://mainnet.infura.io/v3/1234567890', | ||
SIGNER_ENDPOINT: 'http://localhost:8081', | ||
ETHEREUM_ELECTIONS_CONTRACT: '0x1234567890', | ||
NODE_ADDRESS: '555550a3c12e86b4b5f39b213f7e19d048276dae', | ||
MANAGEMENT_SERVICE_ENDPOINT_SCHEMA: 'http', | ||
STATUS_JSON_PATH: '/path/to/status.json', | ||
RUN_LOOP_POLL_TIME_SECONDS: 60, | ||
ETHEREUM_BALANCE_POLL_TIME_SECONDS: 120, | ||
ETHEREUM_CAN_JOIN_COMMITTEE_POLL_TIME_SECONDS: 180, | ||
INVALID_ETHEREUM_SYNC_SECONDS: 240, | ||
REPUTATION_SAMPLE_SIZE: 20, | ||
INVALID_REPUTATION_CHECK_THRESHOLD: 2, | ||
INVALID_REPUTATION_THRESHOLD: 0.5, | ||
ORBS_REPUTATIONS_CONTRACT: '0x987654321', | ||
ETHEREUM_SYNC_REQUIREMENT_SECONDS: 300, | ||
FAIL_TO_SYNC_VCS_TIMEOUT_SECONDS: 360, | ||
ELECTIONS_REFRESH_WINDOW_SECONDS: 420, | ||
INVALID_REPUTATION_GRACE_SECONDS: 480, | ||
VOTE_UNREADY_VALIDITY_SECONDS: 540, | ||
ELECTIONS_AUDIT_ONLY: false, | ||
SUSPEND_VOTE_UNREADY: false, | ||
ETHEREUM_DISCOUNT_GAS_PRICE_FACTOR: 0.8, | ||
ETHEREUM_DISCOUNT_TX_TIMEOUT_SECONDS: 600, | ||
ETHEREUM_NON_DISCOUNT_TX_TIMEOUT_SECONDS: 660, | ||
ETHEREUM_MAX_GAS_PRICE: 1000000000, | ||
ETHEREUM_MAX_COMMITTED_DAILY_TX: 10, | ||
}; | ||
|
||
test('setConfigEnvVars uses environment variables when set', (t) => { | ||
const input: Configuration = { ...exampleConfig }; | ||
|
||
// Need to cast to stop TS complaining about number/string mismatch | ||
process.env = { ...process.env, ...mockEnv } as unknown as NodeJS.ProcessEnv; | ||
|
||
setConfigEnvVars(input); | ||
|
||
for (const key of Object.keys(exampleConfig)) { | ||
// Env var is NODE_ADDRESS, but config is NodeOrbsAddress | ||
if (key === 'NodeOrbsAddress') { | ||
t.assert( | ||
input[key as keyof Configuration] === mockEnv[camelCaseToSnakeCase('NodeAddress') as keyof typeof mockEnv] | ||
); | ||
continue; | ||
} | ||
t.assert(input[key as keyof Configuration] === mockEnv[camelCaseToSnakeCase(key) as keyof typeof mockEnv]); | ||
} | ||
}); | ||
|
||
test('boolean environment variables are parsed correctly', (t) => { | ||
const input: Configuration = { ...exampleConfig }; | ||
|
||
const testCases = [ | ||
{ description: 'No env var set, should use default', envVar: undefined, expected: input.SuspendVoteUnready }, | ||
{ description: 'Env var set to `true`, should resolve to true', envVar: 'true', expected: true }, | ||
{ description: 'Env var set to `false`, should resolve to false', envVar: 'false', expected: false }, | ||
]; | ||
|
||
for (const testCase of testCases) { | ||
process.env.SUSPEND_VOTE_UNREADY = testCase.envVar; | ||
setConfigEnvVars(input); | ||
t.assert(input.SuspendVoteUnready === testCase.expected, testCase.description); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { Configuration } from './config'; | ||
|
||
/** | ||
* Parse required and optional node configuration from environment variables | ||
* | ||
* Environment variables override default configuration values | ||
* | ||
* Validation handled later in `validateConfiguration` | ||
* @param config - The node configuration to update | ||
* */ | ||
export function setConfigEnvVars(config: Configuration): void { | ||
config.ManagementServiceEndpoint = process.env.MANAGEMENT_SERVICE_ENDPOINT ?? config.ManagementServiceEndpoint; | ||
config.EthereumEndpoint = process.env.ETHEREUM_ENDPOINT ?? config.EthereumEndpoint; | ||
config.SignerEndpoint = process.env.SIGNER_ENDPOINT ?? config.SignerEndpoint; | ||
config.EthereumElectionsContract = process.env.ETHEREUM_ELECTIONS_CONTRACT ?? config.EthereumElectionsContract; | ||
// TODO: Rename NodeOrbsAddress globally | ||
config.NodeOrbsAddress = process.env.NODE_ADDRESS ?? config.NodeOrbsAddress; | ||
config.ManagementServiceEndpointSchema = | ||
process.env.MANAGEMENT_SERVICE_ENDPOINT_SCHEMA ?? config.ManagementServiceEndpointSchema; | ||
config.StatusJsonPath = process.env.STATUS_JSON_PATH ?? config.StatusJsonPath; | ||
config.RunLoopPollTimeSeconds = process.env.RUN_LOOP_POLL_TIME_SECONDS | ||
? Number(process.env.RUN_LOOP_POLL_TIME_SECONDS) | ||
: config.RunLoopPollTimeSeconds; | ||
config.EthereumBalancePollTimeSeconds = process.env.ETHEREUM_BALANCE_POLL_TIME_SECONDS | ||
? Number(process.env.ETHEREUM_BALANCE_POLL_TIME_SECONDS) | ||
: config.EthereumBalancePollTimeSeconds; | ||
config.EthereumCanJoinCommitteePollTimeSeconds = process.env.ETHEREUM_CAN_JOIN_COMMITTEE_POLL_TIME_SECONDS | ||
? Number(process.env.ETHEREUM_CAN_JOIN_COMMITTEE_POLL_TIME_SECONDS) | ||
: config.EthereumCanJoinCommitteePollTimeSeconds; | ||
config.InvalidEthereumSyncSeconds = process.env.INVALID_ETHEREUM_SYNC_SECONDS | ||
? Number(process.env.INVALID_ETHEREUM_SYNC_SECONDS) | ||
: config.InvalidEthereumSyncSeconds; | ||
config.ReputationSampleSize = process.env.REPUTATION_SAMPLE_SIZE | ||
? Number(process.env.REPUTATION_SAMPLE_SIZE) | ||
: config.ReputationSampleSize; | ||
config.InvalidReputationCheckThreshold = process.env.INVALID_REPUTATION_CHECK_THRESHOLD | ||
? Number(process.env.INVALID_REPUTATION_CHECK_THRESHOLD) | ||
: config.InvalidReputationCheckThreshold; | ||
config.InvalidReputationThreshold = process.env.INVALID_REPUTATION_THRESHOLD | ||
? Number(process.env.INVALID_REPUTATION_THRESHOLD) | ||
: config.InvalidReputationThreshold; | ||
config.OrbsReputationsContract = process.env.ORBS_REPUTATIONS_CONTRACT ?? config.OrbsReputationsContract; | ||
config.EthereumSyncRequirementSeconds = process.env.ETHEREUM_SYNC_REQUIREMENT_SECONDS | ||
? Number(process.env.ETHEREUM_SYNC_REQUIREMENT_SECONDS) | ||
: config.EthereumSyncRequirementSeconds; | ||
config.FailToSyncVcsTimeoutSeconds = process.env.FAIL_TO_SYNC_VCS_TIMEOUT_SECONDS | ||
? Number(process.env.FAIL_TO_SYNC_VCS_TIMEOUT_SECONDS) | ||
: config.FailToSyncVcsTimeoutSeconds; | ||
config.ElectionsRefreshWindowSeconds = process.env.ELECTIONS_REFRESH_WINDOW_SECONDS | ||
? Number(process.env.ELECTIONS_REFRESH_WINDOW_SECONDS) | ||
: config.ElectionsRefreshWindowSeconds; | ||
config.InvalidReputationGraceSeconds = process.env.INVALID_REPUTATION_GRACE_SECONDS | ||
? Number(process.env.INVALID_REPUTATION_GRACE_SECONDS) | ||
: config.InvalidReputationGraceSeconds; | ||
config.VoteUnreadyValiditySeconds = process.env.VOTE_UNREADY_VALIDITY_SECONDS | ||
? Number(process.env.VOTE_UNREADY_VALIDITY_SECONDS) | ||
: config.VoteUnreadyValiditySeconds; | ||
config.ElectionsAuditOnly = process.env.ELECTIONS_AUDIT_ONLY | ||
? process.env.ELECTIONS_AUDIT_ONLY === 'true' | ||
: config.ElectionsAuditOnly; | ||
config.SuspendVoteUnready = process.env.SUSPEND_VOTE_UNREADY | ||
? process.env.SUSPEND_VOTE_UNREADY === 'true' | ||
: config.SuspendVoteUnready; | ||
config.EthereumDiscountGasPriceFactor = process.env.ETHEREUM_DISCOUNT_GAS_PRICE_FACTOR | ||
? Number(process.env.ETHEREUM_DISCOUNT_GAS_PRICE_FACTOR) | ||
: config.EthereumDiscountGasPriceFactor; | ||
config.EthereumDiscountTxTimeoutSeconds = process.env.ETHEREUM_DISCOUNT_TX_TIMEOUT_SECONDS | ||
? Number(process.env.ETHEREUM_DISCOUNT_TX_TIMEOUT_SECONDS) | ||
: config.EthereumDiscountTxTimeoutSeconds; | ||
config.EthereumNonDiscountTxTimeoutSeconds = process.env.ETHEREUM_NON_DISCOUNT_TX_TIMEOUT_SECONDS | ||
? Number(process.env.ETHEREUM_NON_DISCOUNT_TX_TIMEOUT_SECONDS) | ||
: config.EthereumNonDiscountTxTimeoutSeconds; | ||
config.EthereumMaxGasPrice = process.env.ETHEREUM_MAX_GAS_PRICE | ||
? Number(process.env.ETHEREUM_MAX_GAS_PRICE) | ||
: config.EthereumMaxGasPrice; | ||
config.EthereumMaxCommittedDailyTx = process.env.ETHEREUM_MAX_COMMITTED_DAILY_TX | ||
? Number(process.env.ETHEREUM_MAX_COMMITTED_DAILY_TX) | ||
: config.EthereumMaxCommittedDailyTx; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters