Skip to content

Commit

Permalink
core/plugins: add per-plugin env vars
Browse files Browse the repository at this point in the history
  • Loading branch information
jmank88 committed Jan 5, 2024
1 parent 2f10153 commit 72459a5
Show file tree
Hide file tree
Showing 15 changed files with 117 additions and 29 deletions.
32 changes: 18 additions & 14 deletions core/config/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,27 @@ import (
)

var (
Config = Var("CL_CONFIG")
Config = Var("CL_CONFIG")
DatabaseAllowSimplePasswords = Var("CL_DATABASE_ALLOW_SIMPLE_PASSWORDS")
DatabaseURL = Secret("CL_DATABASE_URL")
DatabaseBackupURL = Secret("CL_DATABASE_BACKUP_URL")
PasswordKeystore = Secret("CL_PASSWORD_KEYSTORE")
PasswordVRF = Secret("CL_PASSWORD_VRF")
PyroscopeAuthToken = Secret("CL_PYROSCOPE_AUTH_TOKEN")
PrometheusAuthToken = Secret("CL_PROMETHEUS_AUTH_TOKEN")
ThresholdKeyShare = Secret("CL_THRESHOLD_KEY_SHARE")
// Migrations env vars
EVMChainIDNotNullMigration0195 = "CL_EVM_CHAINID_NOT_NULL_MIGRATION_0195"
)

// LOOPP commands and vars
// LOOPP commands and vars
var (
MedianPluginCmd = Var("CL_MEDIAN_CMD")
MedianPluginEnv = Var("CL_MEDIAN_ENV")
SolanaPluginCmd = Var("CL_SOLANA_CMD")
SolanaPluginEnv = Var("CL_SOLANA_ENV")
StarknetPluginCmd = Var("CL_STARKNET_CMD")
StarknetPluginEnv = Var("CL_STARKNET_ENV")
// PrometheusDiscoveryHostName is the externally accessible hostname
// published by the node in the `/discovery` endpoint. Generally, it is expected to match
// the public hostname of node.
Expand All @@ -22,24 +37,13 @@ var (
// In house we observed that the resolved value of os.Hostname was not accessible to
// outside of the given pod
PrometheusDiscoveryHostName = Var("CL_PROMETHEUS_DISCOVERY_HOSTNAME")
// EnvLooopHostName is the hostname used for HTTP communication between the
// LOOPPHostName is the hostname used for HTTP communication between the
// node and LOOPps. In most cases this does not need to be set explicitly.
LOOPPHostName = Var("CL_LOOPP_HOSTNAME")
// Work around for Solana LOOPPs configured with zero values.
MinOCR2MaxDurationQuery = Var("CL_MIN_OCR2_MAX_DURATION_QUERY")
// PipelineOvertime is an undocumented escape hatch for overriding the default padding in pipeline executions.
PipelineOvertime = Var("CL_PIPELINE_OVERTIME")

DatabaseAllowSimplePasswords = Var("CL_DATABASE_ALLOW_SIMPLE_PASSWORDS")
DatabaseURL = Secret("CL_DATABASE_URL")
DatabaseBackupURL = Secret("CL_DATABASE_BACKUP_URL")
PasswordKeystore = Secret("CL_PASSWORD_KEYSTORE")
PasswordVRF = Secret("CL_PASSWORD_VRF")
PyroscopeAuthToken = Secret("CL_PYROSCOPE_AUTH_TOKEN")
PrometheusAuthToken = Secret("CL_PROMETHEUS_AUTH_TOKEN")
ThresholdKeyShare = Secret("CL_THRESHOLD_KEY_SHARE")
// Migrations env vars
EVMChainIDNotNullMigration0195 = "CL_EVM_CHAINID_NOT_NULL_MIGRATION_0195"
)

type Var string
Expand Down
1 change: 1 addition & 0 deletions core/scripts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ require (
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/gtank/ristretto255 v0.1.2 // indirect
github.com/hashicorp/go-envparse v0.1.0 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-plugin v1.5.2 // indirect
Expand Down
2 changes: 2 additions & 0 deletions core/scripts/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY=
github.com/hashicorp/go-envparse v0.1.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc=
github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY=
github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
Expand Down
11 changes: 10 additions & 1 deletion core/services/chainlink/relayer_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,14 @@ func (r *RelayerFactory) NewSolana(ks keystore.Solana, chainCfgs solana.TOMLConf
if err != nil {
return nil, fmt.Errorf("failed to marshal Solana configs: %w", err)
}

envVars, err := plugins.ParseEnvFile(env.SolanaPluginEnv.Get())
if err != nil {
return nil, fmt.Errorf("failed to parse Solana env file: %w", err)
}
solCmdFn, err := plugins.NewCmdFactory(r.Register, plugins.CmdConfig{
ID: relayID.Name(),
Cmd: cmdName,
Env: envVars,
})
if err != nil {
return nil, fmt.Errorf("failed to create Solana LOOP command: %w", err)
Expand Down Expand Up @@ -197,9 +201,14 @@ func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs config.TOML
return nil, fmt.Errorf("failed to marshal StarkNet configs: %w", err)
}

envVars, err := plugins.ParseEnvFile(env.StarknetPluginEnv.Get())
if err != nil {
return nil, fmt.Errorf("failed to parse Starknet env file: %w", err)
}
starknetCmdFn, err := plugins.NewCmdFactory(r.Register, plugins.CmdConfig{
ID: relayID.Name(),
Cmd: cmdName,
Env: envVars,
})
if err != nil {
return nil, fmt.Errorf("failed to create StarkNet LOOP command: %w", err)
Expand Down
21 changes: 20 additions & 1 deletion core/services/ocr2/delegate.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
ocr2keepers20runner "github.com/smartcontractkit/chainlink-automation/pkg/v2/runner"
ocr2keepers21config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config"
ocr2keepers21 "github.com/smartcontractkit/chainlink-automation/pkg/v3/plugin"
"github.com/smartcontractkit/chainlink/v2/core/config/env"

"github.com/smartcontractkit/chainlink-vrf/altbn_128"
dkgpkg "github.com/smartcontractkit/chainlink-vrf/dkg"
Expand Down Expand Up @@ -588,8 +589,26 @@ func (d *Delegate) newServicesGenericPlugin(
OffchainConfigDigester: provider.OffchainConfigDigester(),
}

var envVars []string
switch p.PluginName {
case "median":
envVars, err = plugins.ParseEnvFile(env.MedianPluginEnv.Get())
if err != nil {
return nil, fmt.Errorf("failed to parse median env file: %w", err)
}
}
if len(p.EnvVars) > 0 {
for k, v := range p.EnvVars {
envVars = append(envVars, k+"="+v)
}
}

pluginLggr := lggr.Named(p.PluginName).Named(spec.ContractID).Named(spec.GetID())
cmdFn, grpcOpts, err := d.cfg.RegisterLOOP(fmt.Sprintf("%s-%s-%s", p.PluginName, spec.ContractID, spec.GetID()), command)
cmdFn, grpcOpts, err := d.cfg.RegisterLOOP(plugins.CmdConfig{
ID: fmt.Sprintf("%s-%s-%s", p.PluginName, spec.ContractID, spec.GetID()),
Cmd: command,
Env: envVars,
})
if err != nil {
return nil, fmt.Errorf("failed to register loop: %w", err)
}
Expand Down
12 changes: 11 additions & 1 deletion core/services/ocr2/plugins/median/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,17 @@ func NewMedianServices(ctx context.Context,
if medianLoopEnabled {
// use unique logger names so we can use it to register a loop
medianLggr := lggr.Named("Median").Named(spec.ContractID).Named(spec.GetID())
cmdFn, telem, err2 := cfg.RegisterLOOP(medianLggr.Name(), medianPluginCmd)
envVars, err2 := plugins.ParseEnvFile(env.MedianPluginEnv.Get())
if err2 != nil {
err = fmt.Errorf("failed to parse median env file: %w", err2)
abort()
return
}
cmdFn, telem, err2 := cfg.RegisterLOOP(plugins.CmdConfig{
ID: medianLggr.Name(),
Cmd: medianPluginCmd,
Env: envVars,
})
if err2 != nil {
err = fmt.Errorf("failed to register loop: %w", err2)
abort()
Expand Down
9 changes: 5 additions & 4 deletions core/services/ocr2/validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,11 @@ type Config struct {
}

type innerConfig struct {
Command string `json:"command"`
ProviderType string `json:"providerType"`
PluginName string `json:"pluginName"`
TelemetryType string `json:"telemetryType"`
Command string `json:"command"`
EnvVars map[string]string `json:"envVars"`
ProviderType string `json:"providerType"`
PluginName string `json:"pluginName"`
TelemetryType string `json:"telemetryType"`
Config
}

Expand Down
6 changes: 6 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- `chainlink health` CLI command and HTML `/health` endpoint, to provide human-readable views of the underlying JSON health data.
- Environment variables `CL_MEDIAN_ENV`, `CL_SOLANA_ENV`, and `CL_STARKNET_ENV` for setting environment variables in LOOP Plugins with an `.env` file.
```
echo "Foo=Bar" >> median.env
echo "Baz=Val" >> median.env
CL_MEDIAN_ENV="median.env"
```

### Fixed

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ require (
github.com/graph-gophers/dataloader v5.0.0+incompatible
github.com/graph-gophers/graphql-go v1.3.0
github.com/hashicorp/consul/sdk v0.14.1
github.com/hashicorp/go-envparse v0.1.0
github.com/hashicorp/go-plugin v1.5.2
github.com/hdevalence/ed25519consensus v0.1.0
github.com/jackc/pgconn v1.14.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY=
github.com/hashicorp/go-envparse v0.1.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc=
github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY=
github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
Expand Down
1 change: 1 addition & 0 deletions integration-tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ require (
github.com/hashicorp/consul/api v1.25.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-envparse v0.1.0 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-msgpack v0.5.5 // indirect
Expand Down
2 changes: 2 additions & 0 deletions integration-tests/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY=
github.com/hashicorp/go-envparse v0.1.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc=
github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY=
github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
Expand Down
6 changes: 4 additions & 2 deletions plugins/utils.go → plugins/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import (

// CmdConfig is configuration used to register the LOOP and generate an exec
type CmdConfig struct {
ID string // unique string used by the node to track the LOOP. typically supplied by the loop logger name
Cmd string // string value of executable to exec
ID string // unique string used by the node to track the LOOP. typically supplied by the loop logger name
Cmd string // string value of executable to exec
Env []string // environment variables as described in [exec.Cmd.Env]
}

// NewCmdFactory is helper to ensure synchronization between the loop registry and os cmd to exec the LOOP
Expand All @@ -19,6 +20,7 @@ func NewCmdFactory(register func(id string) (*RegisteredLoop, error), lcfg CmdCo
}
return func() *exec.Cmd {
cmd := exec.Command(lcfg.Cmd) //#nosec G204 -- we control the value of the cmd so the lint/sec error is a false positive
cmd.Env = append(cmd.Env, lcfg.Env...)
cmd.Env = append(cmd.Env, registeredLoop.EnvCfg.AsCmdEnv()...)
return cmd
}, nil
Expand Down
31 changes: 31 additions & 0 deletions plugins/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package plugins

import (
"os"

"github.com/hashicorp/go-envparse"
)

// ParseEnvFile returns a slice of key/value pairs parsed from the file at filepath.
// As a special case, empty filepath returns nil without error.
func ParseEnvFile(filepath string) ([]string, error) {
if filepath == "" {
return nil, nil
}
f, err := os.Open(filepath)
if err != nil {
return nil, err
}
defer func() {
_ = f.Close()
}()
m, err := envparse.Parse(f)
if err != nil {
return nil, err
}
r := make([]string, 0, len(m))
for k, v := range m {
r = append(r, k+"="+v)
}
return r, nil
}
9 changes: 3 additions & 6 deletions plugins/config.go → plugins/registrar.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

// RegistrarConfig generates contains static configuration inher
type RegistrarConfig interface {
RegisterLOOP(loopId string, cmdName string) (func() *exec.Cmd, loop.GRPCOpts, error)
RegisterLOOP(config CmdConfig) (func() *exec.Cmd, loop.GRPCOpts, error)
}

type registarConfig struct {
Expand All @@ -27,11 +27,8 @@ func NewRegistrarConfig(grpcOpts loop.GRPCOpts, loopRegistrationFn func(loopId s
}

// RegisterLOOP calls the configured loopRegistrationFn. The loopRegistrationFn must act as a global registry for LOOPs and must be idempotent.
func (pc *registarConfig) RegisterLOOP(loopID string, cmdName string) (func() *exec.Cmd, loop.GRPCOpts, error) {
cmdFn, err := NewCmdFactory(pc.loopRegistrationFn, CmdConfig{
ID: loopID,
Cmd: cmdName,
})
func (pc *registarConfig) RegisterLOOP(cfg CmdConfig) (func() *exec.Cmd, loop.GRPCOpts, error) {
cmdFn, err := NewCmdFactory(pc.loopRegistrationFn, cfg)
if err != nil {
return nil, loop.GRPCOpts{}, err
}
Expand Down

0 comments on commit 72459a5

Please sign in to comment.