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  }