github.com/smartcontractkit/chainlink-testing-framework/libs@v0.0.0-20240227141906-ec710b4eb1a3/config/examples/testconfig.go (about) 1 package testconfig 2 3 import ( 4 "encoding/base64" 5 "fmt" 6 "os" 7 "strings" 8 9 "github.com/barkimedes/go-deepcopy" 10 "github.com/google/uuid" 11 "github.com/pelletier/go-toml/v2" 12 "github.com/pkg/errors" 13 "golang.org/x/text/cases" 14 "golang.org/x/text/language" 15 16 ctf_config "github.com/smartcontractkit/chainlink-testing-framework/libs/config" 17 ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/libs/docker/test_env" 18 k8s_config "github.com/smartcontractkit/chainlink-testing-framework/libs/k8s/config" 19 "github.com/smartcontractkit/chainlink-testing-framework/libs/logging" 20 "github.com/smartcontractkit/chainlink-testing-framework/libs/utils/osutil" 21 ) 22 23 type TestConfig struct { 24 ChainlinkImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkImage"` 25 ChainlinkUpgradeImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkUpgradeImage"` 26 Logging *ctf_config.LoggingConfig `toml:"Logging"` 27 Network *ctf_config.NetworkConfig `toml:"Network"` 28 Pyroscope *ctf_config.PyroscopeConfig `toml:"Pyroscope"` 29 PrivateEthereumNetwork *ctf_test_env.EthereumNetwork `toml:"PrivateEthereumNetwork"` 30 ConfigurationName string `toml:"-"` 31 } 32 33 // Saves Test Config to a local file 34 func (c *TestConfig) Save() (string, error) { 35 filePath := fmt.Sprintf("test_config-%s.toml", uuid.New()) 36 37 content, err := toml.Marshal(*c) 38 if err != nil { 39 return "", errors.Wrapf(err, "error marshaling test config") 40 } 41 42 err = os.WriteFile(filePath, content, 0600) 43 if err != nil { 44 return "", errors.Wrapf(err, "error writing test config") 45 } 46 47 return filePath, nil 48 } 49 50 // Returns a deep copy of the Test Config or panics on error 51 func (c TestConfig) MustCopy() TestConfig { 52 return deepcopy.MustAnything(c).(TestConfig) 53 } 54 55 type Common struct { 56 ChainlinkNodeFunding *float64 `toml:"chainlink_node_funding"` 57 } 58 59 func (c *Common) Validate() error { 60 if c.ChainlinkNodeFunding != nil && *c.ChainlinkNodeFunding < 0 { 61 return fmt.Errorf("chainlink node funding must be positive") 62 } 63 64 return nil 65 } 66 67 type Product string 68 69 const ( 70 Automation Product = "automation" 71 Cron Product = "cron" 72 DirectRequest Product = "direct_request" 73 Flux Product = "flux" 74 ForwarderOcr Product = "forwarder_ocr" 75 ForwarderOcr2 Product = "forwarder_ocr2" 76 Functions Product = "functions" 77 Keeper Product = "keeper" 78 LogPoller Product = "log_poller" 79 Node Product = "node" 80 OCR Product = "ocr" 81 OCR2 Product = "ocr2" 82 OCR2VRF Product = "ocr2vrf" 83 RunLog Product = "runlog" 84 VRF Product = "vrf" 85 VRFv2 Product = "vrfv2" 86 VRFv2Plus Product = "vrfv2plus" 87 ) 88 89 const TestTypeEnvVarName = "TEST_TYPE" 90 91 func GetConfigurationNameFromEnv() (string, error) { 92 testType := os.Getenv(TestTypeEnvVarName) 93 if testType == "" { 94 return "", fmt.Errorf("%s env var not set", TestTypeEnvVarName) 95 } 96 97 return cases.Title(language.English, cases.NoLower).String(testType), nil 98 } 99 100 const ( 101 Base64OverrideEnvVarName = k8s_config.EnvBase64ConfigOverride 102 NoKey = "NO_KEY" 103 ) 104 105 func GetConfig(configurationName string, product Product) (TestConfig, error) { 106 logger := logging.GetTestLogger(nil) 107 108 configurationName = strings.ReplaceAll(configurationName, "/", "_") 109 configurationName = strings.ReplaceAll(configurationName, " ", "_") 110 configurationName = cases.Title(language.English, cases.NoLower).String(configurationName) 111 fileNames := []string{ 112 "default.toml", 113 fmt.Sprintf("%s.toml", product), 114 "overrides.toml", 115 } 116 117 testConfig := TestConfig{} 118 testConfig.ConfigurationName = configurationName 119 logger.Debug().Msgf("Will apply configuration named '%s' if it is found in any of the configs", configurationName) 120 121 logger.Info().Msg("Reading and applying configs from file system") 122 for _, fileName := range fileNames { 123 logger.Debug().Msgf("Looking for config file %s", fileName) 124 filePath, err := osutil.FindFile(fileName, osutil.DEFAULT_STOP_FILE_NAME, 2) 125 126 if err != nil && errors.Is(err, os.ErrNotExist) { 127 logger.Debug().Msgf("Config file %s not found", fileName) 128 continue 129 } else if err != nil { 130 return TestConfig{}, errors.Wrapf(err, "error looking for file %s", filePath) 131 } 132 logger.Debug().Str("location", filePath).Msgf("Found config file %s", fileName) 133 134 content, err := readFile(filePath) 135 if err != nil { 136 return TestConfig{}, errors.Wrapf(err, "error reading file %s", filePath) 137 } 138 139 err = ctf_config.BytesToAnyTomlStruct(logger, fileName, configurationName, &testConfig, content) 140 if err != nil { 141 return TestConfig{}, errors.Wrapf(err, "error reading file %s", filePath) 142 } 143 } 144 145 configEncoded, isSet := os.LookupEnv(Base64OverrideEnvVarName) 146 if isSet && configEncoded != "" { 147 logger.Debug().Msgf("Base64 config override from environment variable '%s' found", Base64OverrideEnvVarName) 148 decoded, err := base64.StdEncoding.DecodeString(configEncoded) 149 if err != nil { 150 return TestConfig{}, err 151 } 152 153 err = toml.Unmarshal(decoded, &testConfig) 154 if err != nil { 155 return TestConfig{}, errors.Wrapf(err, "error unmarshaling base64 config") 156 } 157 } else { 158 logger.Debug().Msg("Base64 config override from environment variable not found") 159 } 160 161 // it neede some custom logic, so we do it separately 162 err := testConfig.readNetworkConfiguration() 163 if err != nil { 164 return TestConfig{}, errors.Wrapf(err, "error reading network config") 165 } 166 167 logger.Debug().Msg("Validating test config") 168 err = testConfig.Validate() 169 if err != nil { 170 return TestConfig{}, errors.Wrapf(err, "error validating test config") 171 } 172 173 logger.Debug().Msg("Correct test config constructed successfully") 174 return testConfig, nil 175 } 176 177 func (c *TestConfig) readNetworkConfiguration() error { 178 // currently we need to read that kind of secrets only for network configuration 179 if c == nil { 180 c.Network = &ctf_config.NetworkConfig{} 181 } 182 err := c.Network.Default() 183 if err != nil { 184 return errors.Wrapf(err, "error reading default network config") 185 } 186 187 // this is the only value we need to generate dynamically before starting a new simulated chain 188 if c.PrivateEthereumNetwork != nil && c.PrivateEthereumNetwork.EthereumChainConfig != nil { 189 c.PrivateEthereumNetwork.EthereumChainConfig.GenerateGenesisTimestamp() 190 } 191 192 return nil 193 } 194 195 func (c *TestConfig) Validate() error { 196 if c.ChainlinkImage == nil { 197 return fmt.Errorf("chainlink image config must be set") 198 } 199 if err := c.ChainlinkImage.Validate(); err != nil { 200 return errors.Wrapf(err, "chainlink image config validation failed") 201 } 202 if c.ChainlinkUpgradeImage != nil { 203 if err := c.ChainlinkUpgradeImage.Validate(); err != nil { 204 return errors.Wrapf(err, "chainlink upgrade image config validation failed") 205 } 206 } 207 if err := c.Network.Validate(); err != nil { 208 return errors.Wrapf(err, "network config validation failed") 209 } 210 211 if c.Logging == nil { 212 return fmt.Errorf("logging config must be set") 213 } 214 215 if err := c.Logging.Validate(); err != nil { 216 return errors.Wrapf(err, "logging config validation failed") 217 } 218 219 if c.Pyroscope != nil { 220 if err := c.Pyroscope.Validate(); err != nil { 221 return errors.Wrapf(err, "pyroscope config validation failed") 222 } 223 } 224 225 if c.PrivateEthereumNetwork != nil { 226 if err := c.PrivateEthereumNetwork.Validate(); err != nil { 227 return errors.Wrapf(err, "private ethereum network config validation failed") 228 } 229 } 230 231 return nil 232 } 233 234 func readFile(filePath string) ([]byte, error) { 235 content, err := os.ReadFile(filePath) 236 if err != nil { 237 return nil, errors.Wrapf(err, "error reading file %s", filePath) 238 } 239 240 return content, nil 241 }