github.com/smartcontractkit/chainlink-testing-framework/libs@v0.0.0-20240227141906-ec710b4eb1a3/config/README.md (about) 1 # TOML Config 2 3 These basic building blocks can be used to create a TOML config file. For example: 4 ```golang 5 import ( 6 ctf_config "github.com/smartcontractkit/chainlink-testing-framework/libs/config" 7 ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/libs/docker/test_env" 8 ) 9 10 type TestConfig struct { 11 ChainlinkImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkImage"` 12 ChainlinkUpgradeImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkUpgradeImage"` 13 Logging *ctf_config.LoggingConfig `toml:"Logging"` 14 Network *ctf_config.NetworkConfig `toml:"Network"` 15 Pyroscope *ctf_config.PyroscopeConfig `toml:"Pyroscope"` 16 PrivateEthereumNetwork *ctf_test_env.EthereumNetwork `toml:"PrivateEthereumNetwork"` 17 } 18 ``` 19 20 It's up to the user to provide a way to read the config from file and unmarshal it into the struct. You can check [testconfig.go](../config/examples/testconfig.go) to see one way it chould be done.. 21 22 `Validate()` should be used to ensure that the config is valid. Some of the building blocks have also a `Default()` method that can be used to get default values. 23 24 Also you might find `BytesToAnyTomlStruct(logger zerolog.Logger, filename, configurationName string, target any, content []byte) error` utility method useful for unmarshalling TOMLs read from env var or files into a struct 25 26 ## Secrets in TOML config 27 28 For all values regarded as secrets, their keys should end with the `_secret` suffix. For example, use `basic_auth_secret="basic-auth"` instead of `basic_auth="basic-auth"`. 29 30 ## Working example 31 32 For a full working example making use of all the building blocks see [testconfig.go](../config/examples/testconfig.go). It provides methods for reading TOML, applying overrides and validating non-empty config blocks. It supports 4 levels of overrides, in order of precedence: 33 * `BASE64_CONFIG_OVERRIDE` env var 34 * `overrides.toml` 35 * `[product_name].toml` 36 * `default.toml` 37 38 All you need to do now to get the config is execute `func GetConfig(configurationName string, product string) (TestConfig, error)`. It will first look for folder with file `.root_dir` and from there it will look for config files in all subfolders, so that you can place the config files in whatever folder(s) work for you. It assumes that all configuration versions for a single product are kept in `[product_name].toml` under different configuration names (that can represent anything you want: a single test, a test type, a test group, etc). 39 40 Overrides of config files are done in a super-simple way. We try to unmarshall consecutive files into the same struct. Since it's all pointer based only not-nil keys are overwritten. 41 42 ## IMPORTANT! 43 It is **required** to add `overrides.toml` to `.gitignore` in your project, so that you don't accidentally commit it as it might contain secrets. 44 45 ## Network config (and default RPC endpoints) 46 47 Some more explanation is needed for the `NetworkConfig`: 48 ```golang 49 type NetworkConfig struct { 50 // list of networks that should be used for testing 51 SelectedNetworks []string `toml:"selected_networks"` 52 // map of network name to EVMNetworks where key is network name and value is a pointer to EVMNetwork 53 // if not set, it will try to find the network from defined networks in MappedNetworks under known_networks.go 54 // it doesn't matter if you use `arbitrum_sepolia` or `ARBITRUM_SEPOLIA` or even `arbitrum_SEPOLIA` as key 55 // as all keys will be uppercased when loading the Default config 56 EVMNetworks map[string]*blockchain.EVMNetwork `toml:"EVMNetworks,omitempty"` 57 // map of network name to ForkConfigs where key is network name and value is a pointer to ForkConfig 58 // only used if network fork is needed, if provided, the network will be forked with the given config 59 // networkname is fetched first from the EVMNetworks and 60 // if not defined with EVMNetworks, it will try to find the network from defined networks in MappedNetworks under known_networks.go 61 ForkConfigs map[string]*ForkConfig `toml:"ForkConfigs,omitempty"` 62 // map of network name to RPC endpoints where key is network name and value is a list of RPC HTTP endpoints 63 RpcHttpUrls map[string][]string `toml:"RpcHttpUrls"` 64 // map of network name to RPC endpoints where key is network name and value is a list of RPC WS endpoints 65 RpcWsUrls map[string][]string `toml:"RpcWsUrls"` 66 // map of network name to wallet keys where key is network name and value is a list of private keys (aka funding keys) 67 WalletKeys map[string][]string `toml:"WalletKeys"` 68 } 69 70 func (n *NetworkConfig) Default() error { 71 ... 72 } 73 ``` 74 75 Sample TOML config: 76 ```toml 77 selected_networks = ["arbitrum_goerli", "optimism_goerli", "new_network"] 78 79 [EVMNetworks.new_network] 80 evm_name = "new_test_network" 81 evm_chain_id = 100009 82 evm_simulated = true 83 evm_chainlink_transaction_limit = 5000 84 evm_minimum_confirmations = 1 85 evm_gas_estimation_buffer = 10000 86 client_implementation = "Ethereum" 87 evm_supports_eip1559 = true 88 evm_default_gas_limit = 6000000 89 90 [ForkConfigs.new_network] 91 url = "ws://localhost:8546" 92 block_number = 100 93 94 [RpcHttpUrls] 95 arbitrum_goerli = ["https://devnet-2.mt/ABC/rpc/"] 96 new_network = ["http://localhost:8545"] 97 98 [RpcWsUrls] 99 arbitrum_goerli = ["wss://devnet-2.mt/ABC/ws/"] 100 new_network = ["ws://localhost:8546"] 101 102 [WalletKeys] 103 arbitrum_goerli = ["1810868fc221b9f50b5b3e0186d8a5f343f892e51ce12a9e818f936ec0b651ed"] 104 optimism_goerli = ["1810868fc221b9f50b5b3e0186d8a5f343f892e51ce12a9e818f936ec0b651ed"] 105 new_network = ["ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"] 106 ``` 107 108 Whenver you are adding a new EVMNetwork to the config, you can either 109 - provide the rpcs and wallet keys in Rpc<Http/Ws>Urls and WalletKeys. Like in the example above, you can see that `new_network` is added to the `selected_networks` and `EVMNetworks` and then the rpcs and wallet keys are provided in `RpcHttpUrls`, `RpcWsUrls` and `WalletKeys` respectively. 110 - provide the rpcs and wallet keys in the `EVMNetworks` itself. Like in the example below, you can see that `new_network` is added to the `selected_networks` and `EVMNetworks` and then the rpcs and wallet keys are provided in `EVMNetworks` itself. 111 ```toml 112 113 selected_networks = ["new_network"] 114 115 [EVMNetworks.new_network] 116 evm_name = "new_test_network" 117 evm_chain_id = 100009 118 evm_urls = ["ws://localhost:8546"] 119 evm_http_urls = ["http://localhost:8545"] 120 evm_keys = ["ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"] 121 evm_simulated = true 122 evm_chainlink_transaction_limit = 5000 123 evm_minimum_confirmations = 1 124 evm_gas_estimation_buffer = 10000 125 client_implementation = "Ethereum" 126 evm_supports_eip1559 = true 127 evm_default_gas_limit = 6000000 128 ``` 129 130 131 If your config struct looks like that: 132 ```golang 133 134 type TestConfig struct { 135 Network *ctf_config.NetworkConfig `toml:"Network"` 136 } 137 ``` 138 139 then your TOML file should look like that: 140 ```toml 141 [Network] 142 selected_networks = ["arbitrum_goerli","new_network"] 143 144 [Network.EVMNetworks.new_network] 145 evm_name = "new_test_network" 146 evm_chain_id = 100009 147 evm_simulated = true 148 evm_chainlink_transaction_limit = 5000 149 evm_minimum_confirmations = 1 150 evm_gas_estimation_buffer = 10000 151 client_implementation = "Ethereum" 152 evm_supports_eip1559 = true 153 evm_default_gas_limit = 6000000 154 155 [Network.RpcHttpUrls] 156 arbitrum_goerli = ["https://devnet-2.mt/ABC/rpc/"] 157 new_network = ["http://localhost:8545"] 158 159 [Network.RpcWsUrls] 160 arbitrum_goerli = ["ws://devnet-2.mt/ABC/rpc/"] 161 new_network = ["ws://localhost:8546"] 162 163 [Network.WalletKeys] 164 arbitrum_goerli = ["1810868fc221b9f50b5b3e0186d8a5f343f892e51ce12a9e818f936ec0b651ed"] 165 new_network = ["ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"] 166 ``` 167 168 169 It not only stores the configuration of selected networks and RPC endpoints and wallet keys, but via `Default()` method provides a way to read from env var `BASE64_NETWORK_CONFIG` a base64-ed configuration of RPC endpoints and wallet keys. This could prove useful in the CI, where we could store as a secret a default configuration of stable endpoints, so that when we run a test job all that we have to provide is the network name and nothing more as it's pretty tedious, especially for on-demand jobs, to have to pass the whole RPC/wallet configuration every time you run it. 170 171 If in your product config you want to support case-insensitive network names and map keys remember to run `NetworkConfig.UpperCaseNetworkNames()` on your config before using it. 172 173 ## Providing custom values in the CI 174 175 Up to this point when we wanted to modify some dynamic tests parameters in the CI we would simply set env vars. That approach won't work anymore. The way to go around it is to build a TOML file, `base64` it, mask it and then set is as `BASE64_CONFIG_OVERRIDE` env var that will be read by tests. Here's an example of a working snippet of how that could look: 176 ```bash 177 convert_to_toml_array() { 178 local IFS=',' 179 local input_array=($1) 180 local toml_array_format="[" 181 182 for element in "${input_array[@]}"; do 183 toml_array_format+="\"$element\"," 184 done 185 186 toml_array_format="${toml_array_format%,}]" 187 echo "$toml_array_format" 188 } 189 190 selected_networks=$(convert_to_toml_array "$SELECTED_NETWORKS") 191 log_targets=$(convert_to_toml_array "$LOGSTREAM_LOG_TARGETS") 192 193 if [ -n "$PYROSCOPE_SERVER" ]; then 194 pyroscope_enabled=true 195 else 196 pyroscope_enabled=false 197 fi 198 199 if [ -n "$ETH2_EL_CLIENT" ]; then 200 execution_layer="$ETH2_EL_CLIENT" 201 else 202 execution_layer="geth" 203 fi 204 205 if [ -n "$TEST_LOG_COLLECT" ]; then 206 test_log_collect=true 207 else 208 test_log_collect=false 209 fi 210 211 cat << EOF > config.toml 212 [Network] 213 selected_networks=$selected_networks 214 215 [ChainlinkImage] 216 image="$CHAINLINK_IMAGE" 217 version="$CHAINLINK_VERSION" 218 219 [Pyroscope] 220 enabled=$pyroscope_enabled 221 server_url="$PYROSCOPE_SERVER" 222 environment="$PYROSCOPE_ENVIRONMENT" 223 key_secret="$PYROSCOPE_KEY" 224 225 [Logging] 226 test_log_collect=$test_log_collect 227 run_id="$RUN_ID" 228 229 [Logging.LogStream] 230 log_targets=$log_targets 231 232 [Logging.Loki] 233 tenant_id="$LOKI_TENANT_ID" 234 url="$LOKI_URL" 235 basic_auth_secret="$LOKI_BASIC_AUTH" 236 bearer_token_secret="$LOKI_BEARER_TOKEN" 237 238 [Logging.Grafana] 239 url="$GRAFANA_URL" 240 EOF 241 242 BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) 243 echo ::add-mask::$BASE64_CONFIG_OVERRIDE 244 echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV 245 ``` 246 247 **These two lines in that very order are super important** 248 ```bash 249 BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) 250 echo ::add-mask::$BASE64_CONFIG_OVERRIDE 251 ``` 252 253 `::add-mask::` has to be called only after env var has been set to it's final value, otherwise it won't be recognized and masked properly and secrets will be exposed in the logs. 254 255 ## Providing custom values for local execution 256 For local execution it's best to put custom variables in `overrides.toml` file. 257 258 ## Providing custom values in k8s 259 It's easy. All you need to do is: 260 * Create TOML file with these values 261 * Base64 it: `cat your.toml | base64` 262 * Set the base64 result as `BASE64_CONFIG_OVERRIDE` environment variable. 263 264 Both `BASE64_CONFIG_OVERRIDE` and `BASE64_NETWORK_CONFIG` will be automatically forwarded to k8s (as long as they are set and available to the test process), when creating the environment programmatically via `environment.New()`. 265 266 Quick example: 267 ```bash 268 BASE64_CONFIG_OVERRIDE=$(cat your.toml | base64) go test your-test-that-runs-in-k8s ./file/with/your/test 269 ``` 270 271 # Not moved to TOML 272 Not moved to TOML: 273 * `SLACK_API_KEY` 274 * `SLACK_USER` 275 * `SLACK_CHANNEL` 276 * `TEST_LOG_LEVEL` 277 * `CHAINLINK_ENV_USER` 278 * `DETACH_RUNNER` 279 * `ENV_JOB_IMAGE` 280 * most of k8s-specific env variables were left untouched