Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support default config file overrides #14090

Merged
merged 16 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 74 additions & 5 deletions core/chains/evm/config/toml/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package toml
import (
"bytes"
"embed"
"fmt"
"io"
"log"
"os"
"path/filepath"
"slices"
"strings"
Expand All @@ -12,37 +15,43 @@ import (

"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big"
"github.com/smartcontractkit/chainlink/v2/core/config/env"
)

var (

//go:embed defaults/*.toml
defaultsFS embed.FS
fallback Chain
defaults = map[string]Chain{}
defaultNames = map[string]string{}

customDefaults = map[string]Chain{}

// DefaultIDs is the set of chain ids which have defaults.
DefaultIDs []*big.Big
)

func init() {
// read the defaults first

fes, err := defaultsFS.ReadDir("defaults")
if err != nil {
log.Fatalf("failed to read defaults/: %v", err)
}
for _, fe := range fes {
path := filepath.Join("defaults", fe.Name())
b, err := defaultsFS.ReadFile(path)
if err != nil {
log.Fatalf("failed to read %q: %v", path, err)
b, err2 := defaultsFS.ReadFile(path)
if err2 != nil {
log.Fatalf("failed to read %q: %v", path, err2)
}
var config = struct {
ChainID *big.Big
Chain
}{}

if err := cconfig.DecodeTOML(bytes.NewReader(b), &config); err != nil {
log.Fatalf("failed to decode %q: %v", path, err)
if err3 := cconfig.DecodeTOML(bytes.NewReader(b), &config); err3 != nil {
log.Fatalf("failed to decode %q: %v", path, err3)
}
if fe.Name() == "fallback.toml" {
if config.ChainID != nil {
Expand All @@ -65,6 +74,63 @@ func init() {
slices.SortFunc(DefaultIDs, func(a, b *big.Big) int {
return a.Cmp(b)
})

// read the custom defaults overrides
dir := env.CustomDefaults.Get()
if dir == "" {
// short-circuit; no default overrides provided
return
}

// use evm overrides specifically
evmDir := fmt.Sprintf("%s/evm", dir)

// Read directory contents for evm only
entries, err := os.ReadDir(evmDir)
if err != nil {
log.Fatalf("error reading evm custom defaults override directory: %v", err)
return
}

for _, entry := range entries {
if entry.IsDir() {
// Skip directories
continue
}

path := evmDir + "/" + entry.Name()
file, err := os.Open(path)
if err != nil {
log.Fatalf("error opening file (name: %v) in custom defaults override directory: %v", entry.Name(), err)
}

// Read file contents
b, err := io.ReadAll(file)
file.Close()
if err != nil {
log.Fatalf("error reading file (name: %v) contents in custom defaults override directory: %v", entry.Name(), err)
}

var config = struct {
ChainID *big.Big
Chain
}{}

if err := cconfig.DecodeTOML(bytes.NewReader(b), &config); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct me if I'm wrong but these overrides are specified at runtime it seems? We're not statically embedding the overrides depending on a particular env var (seems hard / impossible now that I think about it since go:embed is on file level vars

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this imply that these defaults need to be manually shipped to nops separately if they're not embedded in the built binary?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but only if not using the official docker release which can bundle them in.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree w/ @jmank88 - The easiest CX I can imagine for the NOPs is including the overrides directory and the env var in the docker image. That way they won't have to think about this at all.

It will take some time before CCIP doesn't release its own image, so this approach seems the easiest from a release perspective as well to me.

log.Fatalf("failed to decode %q in custom defaults override directory: %v", path, err)
}

if config.ChainID == nil {
log.Fatalf("missing ChainID in: %s in custom defaults override directory. exiting", path)
}

id := config.ChainID.String()

if _, ok := customDefaults[id]; ok {
log.Fatalf("%q contains duplicate ChainID: %s", path, id)
}
customDefaults[id] = config.Chain
patrickhuie19 marked this conversation as resolved.
Show resolved Hide resolved
}
}

// DefaultsNamed returns the default Chain values, optionally for the given chainID, as well as a name if the chainID is known.
Expand All @@ -78,6 +144,9 @@ func DefaultsNamed(chainID *big.Big) (c Chain, name string) {
c.SetFrom(&d)
name = defaultNames[s]
}
if overrides, ok := customDefaults[s]; ok {
c.SetFrom(&overrides)
}
return
}

Expand Down
1 change: 1 addition & 0 deletions core/config/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var (
ThresholdKeyShare = Secret("CL_THRESHOLD_KEY_SHARE")
// Migrations env vars
EVMChainIDNotNullMigration0195 = "CL_EVM_CHAINID_NOT_NULL_MIGRATION_0195"
CustomDefaults = Var("CL_CHAIN_DEFAULTS")
jmank88 marked this conversation as resolved.
Show resolved Hide resolved
)

// LOOPP commands and vars
Expand Down
4 changes: 4 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ func TestMain(m *testing.M) {
}))
}

// TestScripts walks through the testdata/scripts directory and runs all tests that end in
// .txt or .txtar with the testscripts library. To run an individual test, specify it in the
// -run param of go test without the txtar or txt suffix, like so:
// go test . -run TestScripts/node/validate/default
func TestScripts(t *testing.T) {
if testing.Short() {
t.Skip("skipping testscript")
Expand Down
Loading
Loading