Skip to content

Commit 2587c25

Browse files
authored
feat: implement env config loading for environment (#351)
This introduces a Config struct for the environment config, which contains the network config and the env config. The network and env config are loaded from the .config directory in the domain. As part of this move, the networks package has been removed, and the code has been moved to the environment package to handle an import cycle.
1 parent 811b2b5 commit 2587c25

File tree

11 files changed

+810
-382
lines changed

11 files changed

+810
-382
lines changed

.changeset/quick-carpets-fry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"chainlink-deployments-framework": minor
3+
---
4+
5+
Adds an `environment.LoadConfig` method to load all config for an environment

engine/cld/domain/domain.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ func (d Domain) ConfigNetworksDirPath() string {
131131
return filepath.Join(d.ConfigDirPath(), DomainConfigNetworksDirName)
132132
}
133133

134+
// ConfigNetworksFilePath returns the path to a domain environment's networks config file.
135+
func (d Domain) ConfigNetworksFilePath(filename string) string {
136+
return filepath.Join(d.ConfigNetworksDirPath(), filename)
137+
}
138+
134139
// ConfigCIDirPath returns the path where the domain's CI .env files are stored.
135140
func (d Domain) ConfigCIDirPath() string {
136141
return filepath.Join(d.ConfigDirPath(), DomainConfigCIDirName)

engine/cld/domain/domain_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,18 @@ func Test_Domain_ConfigLocalFileName(t *testing.T) {
223223
assert.Equal(t, "domains/ccip/.config/local/config.staging.yaml", d.ConfigLocalFilePath("staging"))
224224
}
225225

226-
func Test_Domain_ConfigNetworksFilePath(t *testing.T) {
226+
func Test_Domain_ConfigNetworksDirPath(t *testing.T) {
227227
t.Parallel()
228228
d := NewDomain("domains", "ccip")
229229
assert.Equal(t, "domains/ccip/.config/networks", d.ConfigNetworksDirPath())
230230
}
231231

232+
func Test_Domain_ConfigNetworksFilePath(t *testing.T) {
233+
t.Parallel()
234+
d := NewDomain("domains", "ccip")
235+
assert.Equal(t, "domains/ccip/.config/networks/networks.yaml", d.ConfigNetworksFilePath("networks.yaml"))
236+
}
237+
232238
func Test_Domain_ConfigCIDirPath(t *testing.T) {
233239
t.Parallel()
234240
d := NewDomain("domains", "ccip")

engine/cld/environment/config.go

Lines changed: 177 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,187 @@
11
package environment
22

33
import (
4-
cldf_config_env "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/env"
5-
cldf_config_network "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/network"
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
8+
"github.com/smartcontractkit/chainlink-common/pkg/logger"
9+
10+
config_domain "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/domain"
11+
config_env "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/env"
12+
config_network "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/network"
13+
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/domain"
614
)
715

816
// Config consolidates all the config that is required to be loaded for a domain environment.
917
//
1018
// Specifically it contains the network config and secrets which is loaded from files or env vars.
1119
type Config struct {
12-
Networks *cldf_config_network.Config // The network config loaded from the network manifest file
13-
Env *cldf_config_env.Config // The cld engine's environment config
20+
Networks *config_network.Config // The network config loaded from the network manifest file
21+
Env *config_env.Config // The cld engine's environment config
22+
}
23+
24+
// LoadConfig loads and consolidates all configuration required for a domain environment, including
25+
// network configuration and environment-specific settings.n.
26+
func LoadConfig(dom domain.Domain, env string, lggr logger.Logger) (*Config, error) {
27+
// Load the network manifest for the domain environment.
28+
networks, err := LoadNetworks(env, dom, lggr)
29+
if err != nil {
30+
return nil, fmt.Errorf("failed to load networks: %w", err)
31+
}
32+
33+
envCfg, err := LoadEnvConfig(dom, env)
34+
if err != nil {
35+
return nil, fmt.Errorf("failed to load env config: %w", err)
36+
}
37+
38+
return &Config{
39+
Networks: networks,
40+
Env: envCfg,
41+
}, nil
42+
}
43+
44+
// LoadEnvConfig retrieves the environment configuration for a given domain and environment.
45+
//
46+
// Loading strategy:
47+
// - In CI environments: Loads configuration exclusively from environment variables set by the CI pipeline.
48+
// - In local development: Loads configuration from a local config file specific to the domain and environment.
49+
func LoadEnvConfig(dom domain.Domain, env string) (*config_env.Config, error) {
50+
if isCI() {
51+
cfg, err := config_env.LoadEnv()
52+
if err != nil {
53+
return nil, fmt.Errorf("failed to load env config: %w", err)
54+
}
55+
56+
return cfg, nil
57+
}
58+
59+
fp := filepath.Join(dom.ConfigLocalFilePath(env))
60+
61+
return config_env.LoadFile(fp)
62+
}
63+
64+
// LoadNetworks retrieves the network configuration for the given domain and filters the networks
65+
// according to the specified environment. This ensures that only networks relevant to the selected
66+
// environment are accessible, minimizing the risk of accidental operations on unintended networks.
67+
func LoadNetworks(
68+
env string, dom domain.Domain, lggr logger.Logger,
69+
) (*config_network.Config, error) {
70+
cfg, err := loadNetworkConfig(dom)
71+
if err != nil {
72+
return nil, fmt.Errorf("failed to load network config: %w", err)
73+
}
74+
75+
// Try to load from domain config first
76+
domainConfigPath := filepath.Join(dom.ConfigDomainFilePath())
77+
if _, statErr := os.Stat(domainConfigPath); statErr != nil {
78+
// Domain config doesn't exist, use legacy logic
79+
networkTypes, legacyErr := getLegacyNetworkTypes(env, dom, lggr)
80+
if legacyErr != nil {
81+
return nil, fmt.Errorf("failed to determine network types: %w", legacyErr)
82+
}
83+
lggr.Infof("Loaded %s Networks for %s/%s", networkTypes, dom.Key(), env)
84+
85+
return cfg.FilterWith(config_network.TypesFilter(networkTypes...)), nil
86+
}
87+
88+
// Happy path: domain config exists, try to load it
89+
networkTypes, err := loadDomainConfigNetworkTypes(env, dom)
90+
if err != nil {
91+
lggr.Warnf("Failed to load domain config, falling back to legacy logic: %v", err)
92+
networkTypes, err = getLegacyNetworkTypes(env, dom, lggr)
93+
if err != nil {
94+
return nil, fmt.Errorf("failed to determine network types: %w", err)
95+
}
96+
}
97+
98+
lggr.Infof("Loaded %s Networks for %s/%s", networkTypes, dom.Key(), env)
99+
100+
return cfg.FilterWith(config_network.TypesFilter(networkTypes...)), nil
101+
}
102+
103+
// loadNetworkConfig loads the network config from the .config directory in the given domain.
104+
func loadNetworkConfig(domain domain.Domain) (*config_network.Config, error) {
105+
// Check if the .config directory exists in the domain
106+
configDir := filepath.Join(domain.DirPath(), ".config")
107+
if _, err := os.Stat(configDir); err != nil {
108+
return nil, fmt.Errorf("cannot find config directory: %w", err)
109+
}
110+
111+
// Find all yaml config files in the .config directory and any subdirectories
112+
var configFiles []string
113+
114+
yamlFiles, err := filepath.Glob(filepath.Join(configDir, "**", "*.yaml"))
115+
if err != nil {
116+
return nil, fmt.Errorf("failed to find config files: %w", err)
117+
}
118+
configFiles = append(configFiles, yamlFiles...)
119+
120+
ymlFiles, err := filepath.Glob(filepath.Join(configDir, "**", "*.yml"))
121+
if err != nil {
122+
return nil, fmt.Errorf("failed to find config files: %w", err)
123+
}
124+
configFiles = append(configFiles, ymlFiles...)
125+
126+
if len(configFiles) == 0 {
127+
return nil, fmt.Errorf("no config files found in %s", configDir)
128+
}
129+
130+
cfg, err := config_network.Load(configFiles)
131+
if err != nil {
132+
return nil, fmt.Errorf("failed to load config files: %w", err)
133+
}
134+
135+
return cfg, nil
136+
}
137+
138+
// loadDomainConfigNetworkTypes loads network types from domain config for the given environment.
139+
func loadDomainConfigNetworkTypes(env string, dom domain.Domain) ([]config_network.NetworkType, error) {
140+
domainConfigPath := filepath.Join(dom.ConfigDomainFilePath())
141+
domainConfig, err := config_domain.Load(domainConfigPath)
142+
if err != nil {
143+
return nil, fmt.Errorf("failed to load domain config: %w", err)
144+
}
145+
146+
envConfig, ok := domainConfig.Environments[env]
147+
if !ok {
148+
return nil, fmt.Errorf("environment %s not found in domain config", env)
149+
}
150+
151+
networkTypes := make([]config_network.NetworkType, 0, len(envConfig.NetworkTypes))
152+
for _, networkType := range envConfig.NetworkTypes {
153+
networkTypes = append(networkTypes, config_network.NetworkType(networkType))
154+
}
155+
156+
return networkTypes, nil
157+
}
158+
159+
// getLegacyNetworkTypes returns network types based on legacy switch logic.
160+
func getLegacyNetworkTypes(env string, dom domain.Domain, lggr logger.Logger) ([]config_network.NetworkType, error) {
161+
var networkTypes []config_network.NetworkType
162+
switch env {
163+
case Local, StagingTestnet, ProdTestnet:
164+
networkTypes = []config_network.NetworkType{config_network.NetworkTypeTestnet}
165+
case StagingMainnet, ProdMainnet:
166+
networkTypes = []config_network.NetworkType{config_network.NetworkTypeMainnet}
167+
case Prod:
168+
networkTypes = []config_network.NetworkType{config_network.NetworkTypeTestnet, config_network.NetworkTypeMainnet}
169+
// The following environments are legacy environments that are used to support domains which
170+
// have not transitioned to the new environment structure.
171+
case Testnet, SolStaging:
172+
networkTypes = []config_network.NetworkType{config_network.NetworkTypeTestnet}
173+
case Staging:
174+
if dom.Key() == "data-streams" {
175+
networkTypes = []config_network.NetworkType{config_network.NetworkTypeTestnet, config_network.NetworkTypeMainnet}
176+
} else {
177+
networkTypes = []config_network.NetworkType{config_network.NetworkTypeTestnet}
178+
}
179+
case Mainnet:
180+
networkTypes = []config_network.NetworkType{config_network.NetworkTypeMainnet}
181+
default:
182+
lggr.Errorf("Unknown environment: %s", env)
183+
return nil, fmt.Errorf("unknown env: %s", env)
184+
}
185+
186+
return networkTypes, nil
14187
}

0 commit comments

Comments
 (0)