diff --git a/GNUmakefile b/GNUmakefile index 7e412a5f813..2a49ca2a023 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -57,16 +57,12 @@ chainlink-test: operator-ui ## Build a test build of chainlink binary. chainlink-local-start: ./chainlink -c /etc/node-secrets-volume/default.toml -c /etc/node-secrets-volume/overrides.toml -secrets /etc/node-secrets-volume/secrets.toml node start -d -p /etc/node-secrets-volume/node-password -a /etc/node-secrets-volume/apicredentials --vrfpassword=/etc/node-secrets-volume/apicredentials -.PHONY: install-solana -install-solana: ## Build & install the chainlink-solana binary. - go install $(GOFLAGS) ./plugins/cmd/chainlink-solana - .PHONY: install-median install-median: ## Build & install the chainlink-median binary. go install $(GOFLAGS) ./plugins/cmd/chainlink-median .PHONY: install-starknet -install-starknet: ## Build & install the chainlink-solana binary. +install-starknet: ## Build & install the chainlink-starknet binary. go install $(GOFLAGS) ./plugins/cmd/chainlink-starknet .PHONY: docker ## Build the chainlink docker image diff --git a/core/chains/cosmos/relayer_adapter.go b/core/chains/cosmos/relayer_adapter.go index ffe4181ceb0..ace441c2bb5 100644 --- a/core/chains/cosmos/relayer_adapter.go +++ b/core/chains/cosmos/relayer_adapter.go @@ -37,7 +37,7 @@ type LoopRelayerChain struct { } func NewLoopRelayerChain(r *pkgcosmos.Relayer, s adapters.Chain) *LoopRelayerChain { - ra := relay.NewRelayerServerAdapter(r, s) + ra := relay.NewServerAdapter(r, s) return &LoopRelayerChain{ Relayer: ra, chain: s, diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index a44b6367193..f578604db33 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -37,7 +37,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/sessions" @@ -46,6 +45,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/store/dialects" "github.com/smartcontractkit/chainlink/v2/core/store/migrate" "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/web" webPresenters "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) @@ -656,9 +656,9 @@ func (p *HealthCheckPresenter) ToRow() []string { var status string switch p.Status { - case services.StatusFailing: + case web.HealthStatusFailing: status = red(p.Status) - case services.StatusPassing: + case web.HealthStatusPassing: status = green(p.Status) } diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 91d6be046aa..9d23ad94191 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -302,8 +302,8 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231019215252-bcced69e26a6 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231016152201-a10a460e67d8 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20230901115736-bbabe542a918 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index df634e22a23..b1871711af8 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1458,10 +1458,10 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 h1:vdieOW3CZGdD2R5zvCSMS+0vksyExPN3/Fa1uVfld/A= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47/go.mod h1:xMwqRdj5vqYhCJXgKVqvyAwdcqM6ZAEhnwEQ4Khsop8= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231019215252-bcced69e26a6 h1:nLIqyl3GAOaa2WKqLItQ+dpLEp1XRPZyzADLSMXNWgE= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231019215252-bcced69e26a6/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231016152201-a10a460e67d8 h1:EYo1yJAwBt2RcK45vaExh5cnEJ3nk2RwF0tdNjMoWNI= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231016152201-a10a460e67d8/go.mod h1:S17dRqwSFSrMdueQ3clPI6XWZWKJjTvqTZVd0F05Ugc= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9 h1:fFD5SgSJtnXvkGLK3CExNKpUIz4sGrNNkKv3Ljw63Hk= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20230901115736-bbabe542a918 h1:ByVauKFXphRlSNG47lNuxZ9aicu+r8AoNp933VRPpCw= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20230901115736-bbabe542a918/go.mod h1:/yp/sqD8Iz5GU5fcercjrw0ivJF7HDcupYg+Gjr7EPg= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index b0d49082328..354f0479042 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/loop" + relayservices "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/build" @@ -386,7 +387,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { globalLogger.Debug("Off-chain reporting v2 disabled") } - healthChecker := services.NewChecker() + healthChecker := relayservices.NewChecker() var lbs []utils.DependentAwaiter for _, c := range legacyEVMChains.Slice() { diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 1a46461d6d8..ae0863e3b33 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -144,7 +144,7 @@ func (r *RelayerFactory) NewSolana(ks keystore.Solana, chainCfgs solana.TOMLConf if err != nil { return nil, err } - solanaRelayers[relayID] = relay.NewRelayerServerAdapter(pkgsolana.NewRelayer(lggr, chain), chain) + solanaRelayers[relayID] = relay.NewServerAdapter(pkgsolana.NewRelayer(lggr, chain), chain) } } return solanaRelayers, nil @@ -214,7 +214,7 @@ func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs starknet.St return nil, err } - starknetRelayers[relayID] = relay.NewRelayerServerAdapter(pkgstarknet.NewRelayer(lggr, chain), chain) + starknetRelayers[relayID] = relay.NewServerAdapter(pkgstarknet.NewRelayer(lggr, chain), chain) } } return starknetRelayers, nil diff --git a/core/services/checkable.go b/core/services/checkable.go index e3e4c50386b..7365d3f5999 100644 --- a/core/services/checkable.go +++ b/core/services/checkable.go @@ -1,39 +1,11 @@ package services import ( - "errors" - "testing" + "github.com/smartcontractkit/chainlink-relay/pkg/services" ) -// Checkable should be implemented by any type requiring health checks. -// From the k8s docs: -// > ready means it’s initialized and healthy means that it can accept traffic in kubernetes -// See: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ -type Checkable interface { - // Ready should return nil if ready, or an error message otherwise. - Ready() error - // HealthReport returns a full health report of the callee including it's dependencies. - // key is the dep name, value is nil if healthy, or error message otherwise. - // See CopyHealth. - HealthReport() map[string]error - // Name returns the fully qualified name of the component. Usually the logger name. - Name() string -} +// Deprecated: use services.HealthReporter +type Checkable = services.HealthReporter -// CopyHealth copies health statuses from src to dest. -// If duplicate names are encountered, the errors are joined, unless testing in which case a panic is thrown. -func CopyHealth(dest, src map[string]error) { - for name, err := range src { - errOrig, ok := dest[name] - if ok { - if testing.Testing() { - panic("service names must be unique: duplicate name: " + name) - } - if errOrig != nil { - dest[name] = errors.Join(errOrig, err) - continue - } - } - dest[name] = err - } -} +// Deprecated: use services.CopyHealth +func CopyHealth(dest, src map[string]error) { services.CopyHealth(dest, src) } diff --git a/core/services/health.go b/core/services/health.go index 823c4421db1..6f1ba007a73 100644 --- a/core/services/health.go +++ b/core/services/health.go @@ -4,242 +4,34 @@ import ( "context" "fmt" "net/http" - "sync" - "testing" "time" "github.com/pkg/errors" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/static" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) -//go:generate mockery --quiet --name Checker --output ./mocks/ --case=underscore -type ( - // Checker provides a service which can be probed for system health. - Checker interface { - // Register a service for health checks. - Register(service Checkable) error - // Unregister a service. - Unregister(name string) error - // IsReady returns the current readiness of the system. - // A system is considered ready if all checks are passing (no errors) - IsReady() (ready bool, errors map[string]error) - // IsHealthy returns the current health of the system. - // A system is considered healthy if all checks are passing (no errors) - IsHealthy() (healthy bool, errors map[string]error) - - Start() error - Close() error - } - - checker struct { - srvMutex sync.RWMutex - services map[string]Checkable - stateMutex sync.RWMutex - healthy map[string]error - ready map[string]error - - chStop chan struct{} - chDone chan struct{} - - utils.StartStopOnce - } - - Status string -) - -var _ Checker = (*checker)(nil) - -const ( - StatusPassing Status = "passing" - StatusFailing Status = "failing" - - interval = 15 * time.Second -) - -var ( - healthStatus = promauto.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "health", - Help: "Health status by service", - }, - []string{"service_id"}, - ) - uptimeSeconds = promauto.NewCounter( - prometheus.CounterOpts{ - Name: "uptime_seconds", - Help: "Uptime of the application measured in seconds", - }, - ) - nodeVersion = promauto.NewCounterVec( - prometheus.CounterOpts{ - Name: "version", - Help: "Node version information", - }, - []string{"version", "commit"}, - ) -) - -func NewChecker() Checker { - c := &checker{ - services: make(map[string]Checkable, 10), - healthy: make(map[string]error, 10), - ready: make(map[string]error, 10), - chStop: make(chan struct{}), - chDone: make(chan struct{}), - } - - return c -} +var _ Checker = (*services.HealthChecker)(nil) -func (c *checker) Start() error { - return c.StartOnce("HealthCheck", func() error { - nodeVersion.WithLabelValues(static.Version, static.Sha).Inc() - - // update immediately - c.update() - - go c.run() - - return nil - }) -} - -func (c *checker) Close() error { - return c.StopOnce("HealthCheck", func() error { - close(c.chStop) - <-c.chDone - return nil - }) -} - -func (c *checker) run() { - defer close(c.chDone) - - ticker := time.NewTicker(interval) - - for { - select { - case <-ticker.C: - c.update() - case <-c.chStop: - return - } - } - -} - -func (c *checker) update() { - healthy := make(map[string]error) - ready := make(map[string]error) - - c.srvMutex.RLock() - // copy services into a new map to avoid lock contention while doing checks - services := make(map[string]Checkable, len(c.services)) - for name, s := range c.services { - services[name] = s - } - c.srvMutex.RUnlock() - - // now, do all the checks - for name, s := range services { - ready[name] = s.Ready() - hr := s.HealthReport() - for n, err := range hr { - healthy[n] = err - } - - } - - // we use a separate lock to avoid holding the lock over state while talking - // to services - c.stateMutex.Lock() - defer c.stateMutex.Unlock() - for name, r := range ready { - c.ready[name] = r - } - - for name, h := range healthy { - c.healthy[name] = h - - value := 0 - if h == nil { - value = 1 - } - - // report metrics to prometheus - healthStatus.WithLabelValues(name).Set(float64(value)) - } - uptimeSeconds.Add(interval.Seconds()) -} - -func (c *checker) Register(service Checkable) error { - name := service.Name() - if name == "" { - return errors.Errorf("misconfigured check %#v for %v", name, service) - } - - c.srvMutex.Lock() - defer c.srvMutex.Unlock() - if testing.Testing() { - if orig, ok := c.services[name]; ok { - panic(fmt.Errorf("duplicate name %q: service names must be unique: types %T & %T", name, service, orig)) - } - } - c.services[name] = service - return nil -} - -func (c *checker) Unregister(name string) error { - if name == "" { - return errors.Errorf("name cannot be empty") - } - - c.srvMutex.Lock() - defer c.srvMutex.Unlock() - delete(c.services, name) - healthStatus.DeleteLabelValues(name) - return nil -} - -func (c *checker) IsReady() (ready bool, errors map[string]error) { - c.stateMutex.RLock() - defer c.stateMutex.RUnlock() - - ready = true - errors = make(map[string]error, len(c.services)) - - for name, state := range c.ready { - errors[name] = state - - if state != nil { - ready = false - } - } - - return -} - -func (c *checker) IsHealthy() (healthy bool, errors map[string]error) { - c.stateMutex.RLock() - defer c.stateMutex.RUnlock() - - healthy = true - errors = make(map[string]error, len(c.services)) - - for name, state := range c.healthy { - errors[name] = state - - if state != nil { - healthy = false - } - } - - return +// Checker provides a service which can be probed for system health. +// Deprecated: use services.HealthChecker +// +//go:generate mockery --quiet --name Checker --output ./mocks/ --case=underscore +type Checker interface { + // Register a service for health checks. + Register(service Checkable) error + // Unregister a service. + Unregister(name string) error + // IsReady returns the current readiness of the system. + // A system is considered ready if all checks are passing (no errors) + IsReady() (ready bool, errors map[string]error) + // IsHealthy returns the current health of the system. + // A system is considered healthy if all checks are passing (no errors) + IsHealthy() (healthy bool, errors map[string]error) + + Start() error + Close() error } type InBackupHealthReport struct { diff --git a/core/services/health_test.go b/core/services/health_test.go index f8e139af157..3bd0e5d39b2 100644 --- a/core/services/health_test.go +++ b/core/services/health_test.go @@ -5,8 +5,6 @@ import ( "testing" "time" - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" @@ -14,63 +12,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services" ) -var ErrUnhealthy = errors.New("Unhealthy") - -type boolCheck struct { - name string - healthy bool -} - -func (b boolCheck) Name() string { return b.name } - -func (b boolCheck) Ready() error { - if b.healthy { - return nil - } - return errors.New("Not ready") -} - -func (b boolCheck) HealthReport() map[string]error { - if b.healthy { - return map[string]error{b.name: nil} - } - return map[string]error{b.name: ErrUnhealthy} -} - -func TestCheck(t *testing.T) { - for i, test := range []struct { - checks []services.Checkable - healthy bool - expected map[string]error - }{ - {[]services.Checkable{}, true, map[string]error{}}, - - {[]services.Checkable{boolCheck{"0", true}}, true, map[string]error{"0": nil}}, - - {[]services.Checkable{boolCheck{"0", true}, boolCheck{"1", true}}, true, map[string]error{"0": nil, "1": nil}}, - - {[]services.Checkable{boolCheck{"0", true}, boolCheck{"1", false}}, false, map[string]error{"0": nil, "1": ErrUnhealthy}}, - - {[]services.Checkable{boolCheck{"0", true}, boolCheck{"1", false}, boolCheck{"2", false}}, false, map[string]error{ - "0": nil, - "1": ErrUnhealthy, - "2": ErrUnhealthy, - }}, - } { - c := services.NewChecker() - for _, check := range test.checks { - require.NoError(t, c.Register(check)) - } - - require.NoError(t, c.Start()) - - healthy, results := c.IsHealthy() - - assert.Equal(t, test.healthy, healthy, "case %d", i) - assert.Equal(t, test.expected, results, "case %d", i) - } -} - func TestNewInBackupHealthReport(t *testing.T) { lggr, observed := logger.TestLoggerObserved(t, zapcore.InfoLevel) ibhr := services.NewInBackupHealthReport(1234, lggr) diff --git a/core/services/mocks/checker.go b/core/services/mocks/checker.go index 3f61ed367e1..354812d0212 100644 --- a/core/services/mocks/checker.go +++ b/core/services/mocks/checker.go @@ -3,7 +3,7 @@ package mocks import ( - services "github.com/smartcontractkit/chainlink/v2/core/services" + pkgservices "github.com/smartcontractkit/chainlink-relay/pkg/services" mock "github.com/stretchr/testify/mock" ) @@ -79,11 +79,11 @@ func (_m *Checker) IsReady() (bool, map[string]error) { } // Register provides a mock function with given fields: service -func (_m *Checker) Register(service services.Checkable) error { +func (_m *Checker) Register(service pkgservices.HealthReporter) error { ret := _m.Called(service) var r0 error - if rf, ok := ret.Get(0).(func(services.Checkable) error); ok { + if rf, ok := ret.Get(0).(func(pkgservices.HealthReporter) error); ok { r0 = rf(service) } else { r0 = ret.Error(0) diff --git a/core/services/ocr2/plugins/median/plugin.go b/core/services/ocr2/plugins/median/plugin.go index 544e75397b5..b78b75cbfcb 100644 --- a/core/services/ocr2/plugins/median/plugin.go +++ b/core/services/ocr2/plugins/median/plugin.go @@ -11,16 +11,15 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/smartcontractkit/chainlink/v2/plugins" ) type Plugin struct { - plugins.Base + loop.Plugin stop utils.StopChan } func NewPlugin(lggr logger.Logger) *Plugin { - return &Plugin{Base: plugins.Base{Logger: lggr}, stop: make(utils.StopChan)} + return &Plugin{Plugin: loop.Plugin{Logger: lggr}, stop: make(utils.StopChan)} } func (p *Plugin) NewMedianFactory(ctx context.Context, provider types.MedianProvider, dataSource, juelsPerFeeCoin median.DataSource, errorLog loop.ErrorLog) (loop.ReportingPluginFactory, error) { diff --git a/core/services/relay/evm/loop_impl.go b/core/services/relay/evm/loop_impl.go index 8142721ed16..f69660373e6 100644 --- a/core/services/relay/evm/loop_impl.go +++ b/core/services/relay/evm/loop_impl.go @@ -20,7 +20,7 @@ type LoopRelayer struct { var _ loop.Relayer = &LoopRelayer{} func NewLoopRelayServerAdapter(r *Relayer, cs EVMChainRelayerExtender) *LoopRelayer { - ra := relay.NewRelayerServerAdapter(r, cs) + ra := relay.NewServerAdapter(r, cs) return &LoopRelayer{ Relayer: ra, ext: cs, diff --git a/core/services/relay/evm/relayer_extender.go b/core/services/relay/evm/relayer_extender.go index ce638d10cf9..b6ca4d75fb7 100644 --- a/core/services/relay/evm/relayer_extender.go +++ b/core/services/relay/evm/relayer_extender.go @@ -8,19 +8,19 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" + "github.com/smartcontractkit/chainlink-relay/pkg/loop" relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" evmchain "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" ) // ErrNoChains indicates that no EVM chains have been started var ErrNoChains = errors.New("no EVM chains loaded") type EVMChainRelayerExtender interface { - relay.RelayerExt + loop.RelayerExt Chain() evmchain.Chain } diff --git a/core/services/relay/relay.go b/core/services/relay/relay.go index 4ec7373714c..b7b176ce4b2 100644 --- a/core/services/relay/relay.go +++ b/core/services/relay/relay.go @@ -2,13 +2,11 @@ package relay import ( "context" - "errors" "fmt" "regexp" "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink-relay/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/services" ) type Network = string @@ -71,98 +69,18 @@ func (i *ID) UnmarshalString(s string) error { return nil } -// RelayerExt is a subset of [loop.Relayer] for adapting [types.Relayer], typically with a Chain. See [relayerAdapter]. -type RelayerExt interface { - types.ChainService - ID() string -} - -var _ loop.Relayer = (*relayerAdapter)(nil) - -// relayerAdapter adapts a [types.Relayer] and [RelayerExt] to implement [loop.Relayer]. -type relayerAdapter struct { - types.Relayer - RelayerExt -} - -// NewRelayerAdapter returns a [loop.Relayer] adapted from a [types.Relayer] and [RelayerExt]. -// Unlike NewRelayerServerAdapter which is used to adapt non-LOOPP relayers, this is used to adapt -// LOOPP-based relayer which are then server over GRPC (by the relayerServer). -func NewRelayerAdapter(r types.Relayer, e RelayerExt) loop.Relayer { //nolint:staticcheck - return &relayerAdapter{Relayer: r, RelayerExt: e} -} - -func (r *relayerAdapter) NewConfigProvider(ctx context.Context, rargs types.RelayArgs) (types.ConfigProvider, error) { - return r.Relayer.NewConfigProvider(rargs) -} - -func (r *relayerAdapter) NewMedianProvider(ctx context.Context, rargs types.RelayArgs, pargs types.PluginArgs) (types.MedianProvider, error) { - return r.Relayer.NewMedianProvider(rargs, pargs) -} - -func (r *relayerAdapter) NewMercuryProvider(ctx context.Context, rargs types.RelayArgs, pargs types.PluginArgs) (types.MercuryProvider, error) { - return r.Relayer.NewMercuryProvider(rargs, pargs) -} - -func (r *relayerAdapter) NewFunctionsProvider(ctx context.Context, rargs types.RelayArgs, pargs types.PluginArgs) (types.FunctionsProvider, error) { - return r.Relayer.NewFunctionsProvider(rargs, pargs) -} - -func (r *relayerAdapter) NewPluginProvider(ctx context.Context, rargs types.RelayArgs, pargs types.PluginArgs) (types.PluginProvider, error) { - return nil, fmt.Errorf("unexpected call to NewPluginProvider: did you forget to wrap relayerAdapter in a relayerServerAdapter?") -} - -func (r *relayerAdapter) Start(ctx context.Context) error { - var ms services.MultiStart - return ms.Start(ctx, r.RelayerExt, r.Relayer) -} - -func (r *relayerAdapter) Close() error { - return services.CloseAll(r.Relayer, r.RelayerExt) -} - -func (r *relayerAdapter) Name() string { - return fmt.Sprintf("%s-%s", r.Relayer.Name(), r.RelayerExt.Name()) -} - -func (r *relayerAdapter) Ready() (err error) { - return errors.Join(r.Relayer.Ready(), r.RelayerExt.Ready()) -} - -func (r *relayerAdapter) HealthReport() map[string]error { - hr := make(map[string]error) - services.CopyHealth(hr, r.Relayer.HealthReport()) - return hr -} - -func (r *relayerAdapter) NodeStatuses(ctx context.Context, offset, limit int, chainIDs ...string) (nodes []types.NodeStatus, total int, err error) { - if len(chainIDs) > 1 { - return nil, 0, fmt.Errorf("internal error: node statuses expects at most one chain id got %v", chainIDs) - } - if len(chainIDs) == 1 && chainIDs[0] != r.ID() { - return nil, 0, fmt.Errorf("node statuses unexpected chain id got %s want %s", chainIDs[0], r.ID()) - } - - nodes, _, total, err = r.ListNodeStatuses(ctx, int32(limit), "") - if err != nil { - return nil, 0, err - } - if len(nodes) < offset { - return []types.NodeStatus{}, 0, fmt.Errorf("out of range") - } - if limit <= 0 { - limit = len(nodes) - } else if len(nodes) < limit { - limit = len(nodes) - } - return nodes[offset:limit], total, nil +// ServerAdapter extends [loop.RelayerAdapter] by overriding NewPluginProvider to dispatches calls according to `RelayArgs.ProviderType`. +// This should only be used to adapt relayers not running via GRPC in a LOOPP. +type ServerAdapter struct { + loop.RelayerAdapter } -type relayerServerAdapter struct { - *relayerAdapter +// NewServerAdapter returns a new ServerAdapter. +func NewServerAdapter(r types.Relayer, e loop.RelayerExt) *ServerAdapter { + return &ServerAdapter{RelayerAdapter: loop.RelayerAdapter{Relayer: r, RelayerExt: e}} } -func (r *relayerServerAdapter) NewPluginProvider(ctx context.Context, rargs types.RelayArgs, pargs types.PluginArgs) (types.PluginProvider, error) { +func (r *ServerAdapter) NewPluginProvider(ctx context.Context, rargs types.RelayArgs, pargs types.PluginArgs) (types.PluginProvider, error) { switch types.OCR2PluginType(rargs.ProviderType) { case types.Median: return r.NewMedianProvider(ctx, rargs, pargs) @@ -171,20 +89,9 @@ func (r *relayerServerAdapter) NewPluginProvider(ctx context.Context, rargs type case types.Mercury: return r.NewMercuryProvider(ctx, rargs, pargs) case types.DKG, types.OCR2VRF, types.OCR2Keeper, types.GenericPlugin: - return r.relayerAdapter.NewPluginProvider(ctx, rargs, pargs) + return r.RelayerAdapter.NewPluginProvider(ctx, rargs, pargs) case types.CCIPCommit, types.CCIPExecution: return nil, fmt.Errorf("provider type not supported: %s", rargs.ProviderType) } return nil, fmt.Errorf("provider type not recognized: %s", rargs.ProviderType) } - -// NewRelayerServerAdapter returns a [loop.Relayer] adapted from a [types.Relayer] and [RelayerExt]. -// Unlike NewRelayerAdapter, this behaves like the loop `RelayerServer` and dispatches calls -// to `NewPluginProvider` according to the passed in `RelayArgs.ProviderType`. -// This should only be used to adapt relayers not running via GRPC in a LOOPP. -// -// nolint:staticcheck // SA1019 -func NewRelayerServerAdapter(r types.Relayer, e RelayerExt) loop.Relayer { - ra := &relayerAdapter{Relayer: r, RelayerExt: e} - return &relayerServerAdapter{relayerAdapter: ra} -} diff --git a/core/services/relay/relay_test.go b/core/services/relay/relay_test.go index 25ab76adf3b..d3a94773498 100644 --- a/core/services/relay/relay_test.go +++ b/core/services/relay/relay_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/assert" + "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink-relay/pkg/types" ) @@ -85,7 +86,7 @@ func (m *mockRelayer) NewMercuryProvider(rargs types.RelayArgs, pargs types.Plug } type mockRelayerExt struct { - RelayerExt + loop.RelayerExt } func isType[T any](p any) bool { @@ -95,7 +96,7 @@ func isType[T any](p any) bool { func TestRelayerServerAdapter(t *testing.T) { r := &mockRelayer{} - sa := NewRelayerServerAdapter(r, mockRelayerExt{}) + sa := NewServerAdapter(r, mockRelayerExt{}) testCases := []struct { ProviderType string diff --git a/core/web/health_controller.go b/core/web/health_controller.go index 3646331bb82..d6a7edb2340 100644 --- a/core/web/health_controller.go +++ b/core/web/health_controller.go @@ -5,7 +5,6 @@ import ( "github.com/gin-gonic/gin" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) @@ -14,6 +13,11 @@ type HealthController struct { App chainlink.Application } +const ( + HealthStatusPassing = "passing" + HealthStatusFailing = "failing" +) + // NOTE: We only implement the k8s readiness check, *not* the liveness check. Liveness checks are only recommended in cases // where the app doesn't crash itself on panic, and if implemented incorrectly can cause cascading failures. // See the following for more information: @@ -38,11 +42,11 @@ func (hc *HealthController) Readyz(c *gin.Context) { checks := make([]presenters.Check, 0, len(errors)) for name, err := range errors { - status := services.StatusPassing + status := HealthStatusPassing var output string if err != nil { - status = services.StatusFailing + status = HealthStatusFailing output = err.Error() } @@ -74,11 +78,11 @@ func (hc *HealthController) Health(c *gin.Context) { checks := make([]presenters.Check, 0, len(errors)) for name, err := range errors { - status := services.StatusPassing + status := HealthStatusPassing var output string if err != nil { - status = services.StatusFailing + status = HealthStatusFailing output = err.Error() } diff --git a/core/web/loop_registry.go b/core/web/loop_registry.go index 345ff03704e..b94778675e0 100644 --- a/core/web/loop_registry.go +++ b/core/web/loop_registry.go @@ -85,7 +85,7 @@ func metricTarget(hostName string, port int, path string) *targetgroup.Group { } } -// pluginMetricHandlers routes from endpoints published in service discovery to the the backing LOOP endpoint +// pluginMetricHandlers routes from endpoints published in service discovery to the backing LOOP endpoint func (l *LoopRegistryServer) pluginMetricHandler(gc *gin.Context) { pluginName := gc.Param("name") p, ok := l.registry.Get(pluginName) @@ -95,7 +95,7 @@ func (l *LoopRegistryServer) pluginMetricHandler(gc *gin.Context) { } // unlike discovery, this endpoint is internal btw the node and plugin - pluginURL := fmt.Sprintf("http://%s:%d/metrics", l.loopHostName, p.EnvCfg.PrometheusPort()) + pluginURL := fmt.Sprintf("http://%s:%d/metrics", l.loopHostName, p.EnvCfg.PrometheusPort) res, err := l.client.Get(pluginURL) //nolint if err != nil { msg := fmt.Sprintf("plugin metric handler failed to get plugin url %s", html.EscapeString(pluginURL)) diff --git a/core/web/loop_registry_test.go b/core/web/loop_registry_test.go index 99036d8c73e..59a4d0df686 100644 --- a/core/web/loop_registry_test.go +++ b/core/web/loop_registry_test.go @@ -14,17 +14,17 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/plugins" ) type mockLoopImpl struct { t *testing.T - *plugins.PromServer + *loop.PromServer } // test prom var to avoid collision with real chainlink metrics @@ -44,7 +44,7 @@ func configurePromRegistry() { func newMockLoopImpl(t *testing.T, port int) *mockLoopImpl { return &mockLoopImpl{ t: t, - PromServer: plugins.NewPromServer(port, logger.TestLogger(t).Named("mock-loop"), plugins.WithHandler(testHandler)), + PromServer: loop.PromServerOpts{Handler: testHandler}.New(port, logger.TestLogger(t).Named("mock-loop")), } } @@ -91,7 +91,7 @@ func TestLoopRegistry(t *testing.T) { // set up a test prometheus registry and test metric that is used by // our mock loop impl and isolated from the default prom register configurePromRegistry() - mockLoop := newMockLoopImpl(t, loop.EnvCfg.PrometheusPort()) + mockLoop := newMockLoopImpl(t, loop.EnvCfg.PrometheusPort) mockLoop.start() defer mockLoop.close() mockLoop.run() diff --git a/core/web/presenters/check.go b/core/web/presenters/check.go index 4ee6051727e..52e4aa68005 100644 --- a/core/web/presenters/check.go +++ b/core/web/presenters/check.go @@ -1,14 +1,10 @@ package presenters -import ( - "github.com/smartcontractkit/chainlink/v2/core/services" -) - type Check struct { JAID - Name string `json:"name"` - Status services.Status `json:"status"` - Output string `json:"output"` + Name string `json:"name"` + Status string `json:"status"` + Output string `json:"output"` } func (c Check) GetName() string { diff --git a/go.mod b/go.mod index fbcfcfc6e87..e58e33e99c0 100644 --- a/go.mod +++ b/go.mod @@ -68,8 +68,8 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231019215252-bcced69e26a6 - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231016152201-a10a460e67d8 + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9 + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20230901115736-bbabe542a918 github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 github.com/smartcontractkit/ocr2keepers v0.7.27 @@ -93,7 +93,6 @@ require ( go.uber.org/zap v1.26.0 golang.org/x/crypto v0.14.0 golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 - golang.org/x/net v0.17.0 golang.org/x/sync v0.4.0 golang.org/x/term v0.13.0 golang.org/x/text v0.13.0 @@ -360,6 +359,7 @@ require ( go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect golang.org/x/arch v0.3.0 // indirect + golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.13.0 // indirect google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 // indirect diff --git a/go.sum b/go.sum index 83aa5af4bc6..6ba65987a27 100644 --- a/go.sum +++ b/go.sum @@ -1459,10 +1459,10 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 h1:vdieOW3CZGdD2R5zvCSMS+0vksyExPN3/Fa1uVfld/A= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47/go.mod h1:xMwqRdj5vqYhCJXgKVqvyAwdcqM6ZAEhnwEQ4Khsop8= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231019215252-bcced69e26a6 h1:nLIqyl3GAOaa2WKqLItQ+dpLEp1XRPZyzADLSMXNWgE= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231019215252-bcced69e26a6/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231016152201-a10a460e67d8 h1:EYo1yJAwBt2RcK45vaExh5cnEJ3nk2RwF0tdNjMoWNI= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231016152201-a10a460e67d8/go.mod h1:S17dRqwSFSrMdueQ3clPI6XWZWKJjTvqTZVd0F05Ugc= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9 h1:fFD5SgSJtnXvkGLK3CExNKpUIz4sGrNNkKv3Ljw63Hk= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20230901115736-bbabe542a918 h1:ByVauKFXphRlSNG47lNuxZ9aicu+r8AoNp933VRPpCw= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20230901115736-bbabe542a918/go.mod h1:/yp/sqD8Iz5GU5fcercjrw0ivJF7HDcupYg+Gjr7EPg= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 7c84f94925b..f4845209184 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -385,8 +385,8 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231019215252-bcced69e26a6 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231016152201-a10a460e67d8 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20230901115736-bbabe542a918 // indirect github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 5e87dfd12b8..72e7163c4ac 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2364,10 +2364,10 @@ github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc4 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47/go.mod h1:xMwqRdj5vqYhCJXgKVqvyAwdcqM6ZAEhnwEQ4Khsop8= github.com/smartcontractkit/chainlink-env v0.38.3 h1:ZtOnwkG622R0VCTxL5V09AnT/QXhlFwkGTjd0Lsfpfg= github.com/smartcontractkit/chainlink-env v0.38.3/go.mod h1:7z4sw/hN8TxioQCLwFqQdhK3vaOV0a22Qe99z4bRUcg= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231019215252-bcced69e26a6 h1:nLIqyl3GAOaa2WKqLItQ+dpLEp1XRPZyzADLSMXNWgE= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231019215252-bcced69e26a6/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231016152201-a10a460e67d8 h1:EYo1yJAwBt2RcK45vaExh5cnEJ3nk2RwF0tdNjMoWNI= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231016152201-a10a460e67d8/go.mod h1:S17dRqwSFSrMdueQ3clPI6XWZWKJjTvqTZVd0F05Ugc= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9 h1:fFD5SgSJtnXvkGLK3CExNKpUIz4sGrNNkKv3Ljw63Hk= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20230901115736-bbabe542a918 h1:ByVauKFXphRlSNG47lNuxZ9aicu+r8AoNp933VRPpCw= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20230901115736-bbabe542a918/go.mod h1:/yp/sqD8Iz5GU5fcercjrw0ivJF7HDcupYg+Gjr7EPg= github.com/smartcontractkit/chainlink-testing-framework v1.17.12-0.20231018101901-23824db88d36 h1:ow84QG8vEHMvfjGg0RF8HNYh80WcHci6PIenXyY6K8Y= diff --git a/plugins/README.md b/plugins/README.md index 73c39c6c94d..61667d8d392 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -6,9 +6,9 @@ This directory supports Local-Out-Of-Process (LOOP) Plugins, an alternative node separate processes, plug-in via [github.com/hashicorp/go-plugin](https://github.com/hashicorp/go-plugin), and communicate via [GRPC](https://grpc.io). -There are currently two kinds of plugins, and one implementation of each: a Solana Relayer plugin, and a Median Reporting -plugin. The [cmd](cmd) directory contains their `package main`s for now. These can be built via `make install-solana` and -`make install-median`. +There are currently two kinds of plugins: Relayer plugins, and a Median product plugin. The [cmd](cmd) directory contains +some `package main`s for now, and they can be built via `make install-starknet` and `make install-median`. Solana has been +moved to the `chainlink-solana` repo, and these will soon be moved too. ## How to use diff --git a/plugins/chainlink.Dockerfile b/plugins/chainlink.Dockerfile index 6f713e078af..ec0574e1738 100644 --- a/plugins/chainlink.Dockerfile +++ b/plugins/chainlink.Dockerfile @@ -16,10 +16,20 @@ COPY . . # Build the golang binaries RUN make install-chainlink -RUN make install-solana +# Build LOOP Plugins RUN make install-median RUN make install-starknet +RUN go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-solana | xargs -I % ln -s % /chainlink-solana + +# Build image: Solana Plugin +FROM golang:1.21-bullseye as buildsol +RUN go version +WORKDIR /chainlink-solana + +COPY --from=buildgo /chainlink-solana . +RUN go install ./pkg/solana/cmd/chainlink-solana + # Final image: ubuntu with chainlink binary FROM ubuntu:20.04 @@ -34,12 +44,12 @@ RUN curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ && apt-get clean all COPY --from=buildgo /go/bin/chainlink /usr/local/bin/ -COPY --from=buildgo /go/bin/chainlink-solana /usr/local/bin/ -ENV CL_SOLANA_CMD chainlink-solana COPY --from=buildgo /go/bin/chainlink-median /usr/local/bin/ ENV CL_MEDIAN_CMD chainlink-median COPY --from=buildgo /go/bin/chainlink-starknet /usr/local/bin/ ENV CL_STARKNET_CMD chainlink-starknet +COPY --from=buildsol /go/bin/chainlink-solana /usr/local/bin/ +ENV CL_SOLANA_CMD chainlink-solana # Dependency of CosmWasm/wasmd COPY --from=buildgo /go/pkg/mod/github.com/\!cosm\!wasm/wasmvm@v*/internal/api/libwasmvm.*.so /usr/lib/ diff --git a/plugins/cmd/chainlink-median/main.go b/plugins/cmd/chainlink-median/main.go index 00836fa7c24..87815d24d99 100644 --- a/plugins/cmd/chainlink-median/main.go +++ b/plugins/cmd/chainlink-median/main.go @@ -6,7 +6,6 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/median" - "github.com/smartcontractkit/chainlink/v2/plugins" ) const ( @@ -14,7 +13,7 @@ const ( ) func main() { - s := plugins.MustNewStartedServer(loggerName) + s := loop.MustNewStartedServer(loggerName) defer s.Stop() p := median.NewPlugin(s.Logger) diff --git a/plugins/cmd/chainlink-solana/main.go b/plugins/cmd/chainlink-solana/main.go deleted file mode 100644 index 392ec88405e..00000000000 --- a/plugins/cmd/chainlink-solana/main.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import ( - "context" - "fmt" - "strings" - - "github.com/hashicorp/go-plugin" - "github.com/pelletier/go-toml/v2" - - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - pkgsol "github.com/smartcontractkit/chainlink-solana/pkg/solana" - - "github.com/smartcontractkit/chainlink-solana/pkg/solana" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" - "github.com/smartcontractkit/chainlink/v2/plugins" -) - -const ( - loggerName = "PluginSolana" -) - -func main() { - s := plugins.MustNewStartedServer(loggerName) - defer s.Stop() - - p := &pluginRelayer{Base: plugins.Base{Logger: s.Logger}} - defer s.Logger.ErrorIfFn(p.Close, "Failed to close") - - s.MustRegister(p) - - stopCh := make(chan struct{}) - defer close(stopCh) - - plugin.Serve(&plugin.ServeConfig{ - HandshakeConfig: loop.PluginRelayerHandshakeConfig(), - Plugins: map[string]plugin.Plugin{ - loop.PluginRelayerName: &loop.GRPCPluginRelayer{ - PluginServer: p, - BrokerConfig: loop.BrokerConfig{ - StopCh: stopCh, - Logger: s.Logger, - GRPCOpts: s.GRPCOpts, - }, - }, - }, - GRPCServer: s.GRPCOpts.NewServer, - }) -} - -type pluginRelayer struct { - plugins.Base -} - -func (c *pluginRelayer) NewRelayer(ctx context.Context, config string, keystore loop.Keystore) (loop.Relayer, error) { - d := toml.NewDecoder(strings.NewReader(config)) - d.DisallowUnknownFields() - var cfg struct { - Solana solana.TOMLConfig - } - - if err := d.Decode(&cfg); err != nil { - return nil, fmt.Errorf("failed to decode config toml: %w:\n\t%s", err, config) - } - - opts := solana.ChainOpts{ - Logger: c.Logger, - KeyStore: keystore, - } - chain, err := solana.NewChain(&cfg.Solana, opts) - if err != nil { - return nil, fmt.Errorf("failed to create chain: %w", err) - } - ra := relay.NewRelayerAdapter(pkgsol.NewRelayer(c.Logger, chain), chain) - - c.SubService(ra) - - return ra, nil -} diff --git a/plugins/cmd/chainlink-starknet/main.go b/plugins/cmd/chainlink-starknet/main.go index 1052f3c1fc6..5f1569e48cb 100644 --- a/plugins/cmd/chainlink-starknet/main.go +++ b/plugins/cmd/chainlink-starknet/main.go @@ -12,8 +12,6 @@ import ( pkgstarknet "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink" "github.com/smartcontractkit/chainlink/v2/core/chains/starknet" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" - "github.com/smartcontractkit/chainlink/v2/plugins" ) const ( @@ -21,10 +19,10 @@ const ( ) func main() { - s := plugins.MustNewStartedServer(loggerName) + s := loop.MustNewStartedServer(loggerName) defer s.Stop() - p := &pluginRelayer{Base: plugins.Base{Logger: s.Logger}} + p := &pluginRelayer{Plugin: loop.Plugin{Logger: s.Logger}} defer s.Logger.ErrorIfFn(p.Close, "Failed to close") s.MustRegister(p) @@ -49,7 +47,7 @@ func main() { } type pluginRelayer struct { - plugins.Base + loop.Plugin } // NewRelayer implements the Loopp factory method used by the Loopp server to instantiate a starknet relayer @@ -75,7 +73,7 @@ func (c *pluginRelayer) NewRelayer(ctx context.Context, config string, loopKs lo if err != nil { return nil, fmt.Errorf("failed to create chain: %w", err) } - ra := relay.NewRelayerAdapter(pkgstarknet.NewRelayer(c.Logger, chain), chain) + ra := &loop.RelayerAdapter{Relayer: pkgstarknet.NewRelayer(c.Logger, chain), RelayerExt: chain} c.SubService(ra) diff --git a/plugins/config.go b/plugins/config.go index e84a08f1eac..938cfd0d00c 100644 --- a/plugins/config.go +++ b/plugins/config.go @@ -1,12 +1,7 @@ package plugins import ( - "fmt" - "net/url" - "os" "os/exec" - "strconv" - "strings" "github.com/smartcontractkit/chainlink-relay/pkg/loop" ) @@ -42,149 +37,3 @@ func (pc *registarConfig) RegisterLOOP(loopID string, cmdName string) (func() *e } return cmdFn, pc.grpcOpts, nil } - -// EnvConfig is the configuration interface between the application and the LOOP executable. -// The values are fully resolved and static and passed via the environment. -type EnvConfig interface { - PrometheusPort() int - - // Enables trace generation and collection on the plugin - TracingEnabled() bool - - // The logical address of the trace collector - TracingCollectorTarget() string - - // Attributes to be added to the node's tracing context - TracingAttributes() map[string]string - - // The sampling ratio for the node's tracing context - TracingSamplingRatio() float64 -} - -// SetCmdEnvFromConfig sets LOOP-specific vars in the env of the given cmd. -// It also, due to the plugin library, forwards all env vars from the host automatically. -// TODO: BCF-2662: Remove once we can use skipHostEnv in hashicorp/go-plugin v1.5.0 -// This method is consumed by the host. -func SetCmdEnvFromConfig(cmd *exec.Cmd, cfg EnvConfig) { - injectEnv := map[string]string{ - "CL_PROMETHEUS_PORT": strconv.Itoa(cfg.PrometheusPort()), - "TRACING_ENABLED": strconv.FormatBool(cfg.TracingEnabled()), - "TRACING_COLLECTOR_TARGET": cfg.TracingCollectorTarget(), - "TRACING_SAMPLING_RATIO": strconv.FormatFloat(cfg.TracingSamplingRatio(), 'f', -1, 64), - } - - for k, v := range cfg.TracingAttributes() { - injectEnv["TRACING_ATTRIBUTE_"+k] = v - } - - for k, v := range injectEnv { - cmd.Env = append(cmd.Env, k+"="+v) - } -} - -// isTracingEnabled parses and validates the TRACING_ENABLED environment variable. -func getTracingEnabled() (bool, error) { - tracingEnabledString := os.Getenv("TRACING_ENABLED") - if tracingEnabledString == "" { - return false, nil - } - return strconv.ParseBool(tracingEnabledString) -} - -// getValidCollectorTarget validates TRACING_COLLECTOR_TARGET as a URL. -func getValidCollectorTarget() (string, error) { - tracingCollectorTarget := os.Getenv("TRACING_COLLECTOR_TARGET") - _, err := url.ParseRequestURI(tracingCollectorTarget) - if err != nil { - return "", fmt.Errorf("invalid TRACING_COLLECTOR_TARGET: %w", err) - } - return tracingCollectorTarget, nil -} - -// getTracingAttributes collects attributes prefixed with TRACING_ATTRIBUTE_. -func getTracingAttributes() map[string]string { - tracingAttributes := make(map[string]string) - for _, env := range os.Environ() { - if strings.HasPrefix(env, "TRACING_ATTRIBUTE_") { - tracingAttributes[strings.TrimPrefix(env, "TRACING_ATTRIBUTE_")] = os.Getenv(env) - } - } - return tracingAttributes -} - -// getTracingSamplingRatio parses the TRACING_SAMPLING_RATIO environment variable. -// Any errors in parsing result in a sampling ratio of 0.0. -func getTracingSamplingRatio() float64 { - tracingSamplingRatio := os.Getenv("TRACING_SAMPLING_RATIO") - if tracingSamplingRatio == "" { - return 0.0 - } - samplingRatio, err := strconv.ParseFloat(tracingSamplingRatio, 64) - if err != nil { - return 0.0 - } - return samplingRatio -} - -// GetEnvConfig deserializes LOOP-specific environment variables to an EnvConfig. -func GetEnvConfig() (EnvConfig, error) { - promPortStr := os.Getenv("CL_PROMETHEUS_PORT") - promPort, err := strconv.Atoi(promPortStr) - if err != nil { - return nil, fmt.Errorf("failed to parse CL_PROMETHEUS_PORT: %w", err) - } - - tracingEnabled, err := getTracingEnabled() - if err != nil { - return nil, fmt.Errorf("failed to parse TRACING_ENABLED: %w", err) - } - - var tracingCollectorTarget string - var tracingAttributes map[string]string - var tracingSamplingRatio float64 - if tracingEnabled { - tracingCollectorTarget, err = getValidCollectorTarget() - if err != nil { - return nil, err - } - tracingAttributes = getTracingAttributes() - tracingSamplingRatio = getTracingSamplingRatio() - } - - return &envConfig{ - prometheusPort: promPort, - tracingEnabled: tracingEnabled, - tracingCollectorTarget: tracingCollectorTarget, - tracingAttributes: tracingAttributes, - tracingSamplingRatio: tracingSamplingRatio, - }, nil -} - -// envConfig is an implementation of EnvConfig. -type envConfig struct { - prometheusPort int - tracingEnabled bool - tracingCollectorTarget string - tracingAttributes map[string]string - tracingSamplingRatio float64 -} - -func (e *envConfig) PrometheusPort() int { - return e.prometheusPort -} - -func (e *envConfig) TracingEnabled() bool { - return e.tracingEnabled -} - -func (e *envConfig) TracingCollectorTarget() string { - return e.tracingCollectorTarget -} - -func (e *envConfig) TracingAttributes() map[string]string { - return e.tracingAttributes -} - -func (e *envConfig) TracingSamplingRatio() float64 { - return e.tracingSamplingRatio -} diff --git a/plugins/config_test.go b/plugins/config_test.go deleted file mode 100644 index 18f2029051d..00000000000 --- a/plugins/config_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package plugins - -import ( - "os/exec" - "strconv" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestGetEnvConfig(t *testing.T) { - cases := []struct { - name string - envVars map[string]string - expectError bool - expectedPrometheusPort int - expectedTracingEnabled bool - expectedTracingCollectorTarget string - expectedTracingSamplingRatio float64 - }{ - { - name: "All variables set correctly", - envVars: map[string]string{ - "CL_PROMETHEUS_PORT": "8080", - "TRACING_ENABLED": "true", - "TRACING_COLLECTOR_TARGET": "some:target", - "TRACING_SAMPLING_RATIO": "1.0", - "TRACING_ATTRIBUTE_XYZ": "value", - }, - expectError: false, - expectedPrometheusPort: 8080, - expectedTracingEnabled: true, - expectedTracingCollectorTarget: "some:target", - expectedTracingSamplingRatio: 1.0, - }, - { - name: "CL_PROMETHEUS_PORT parse error", - envVars: map[string]string{ - "CL_PROMETHEUS_PORT": "abc", - }, - expectError: true, - }, - { - name: "TRACING_ENABLED parse error", - envVars: map[string]string{ - "CL_PROMETHEUS_PORT": "8080", - "TRACING_ENABLED": "invalid_bool", - }, - expectError: true, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - for k, v := range tc.envVars { - t.Setenv(k, v) - } - - config, err := GetEnvConfig() - - if tc.expectError { - if err == nil { - t.Errorf("Expected error, got nil") - } - } else { - if err != nil { - t.Errorf("Unexpected error: %v", err) - } else { - if config.PrometheusPort() != tc.expectedPrometheusPort { - t.Errorf("Expected Prometheus port %d, got %d", tc.expectedPrometheusPort, config.PrometheusPort()) - } - if config.TracingEnabled() != tc.expectedTracingEnabled { - t.Errorf("Expected tracingEnabled %v, got %v", tc.expectedTracingEnabled, config.TracingEnabled()) - } - if config.TracingCollectorTarget() != tc.expectedTracingCollectorTarget { - t.Errorf("Expected tracingCollectorTarget %s, got %s", tc.expectedTracingCollectorTarget, config.TracingCollectorTarget()) - } - if config.TracingSamplingRatio() != tc.expectedTracingSamplingRatio { - t.Errorf("Expected tracingSamplingRatio %f, got %f", tc.expectedTracingSamplingRatio, config.TracingSamplingRatio()) - } - } - } - }) - } -} - -// Mock EnvConfig -type MockEnvConfig struct{} - -func (m *MockEnvConfig) PrometheusPort() int { return 9090 } -func (m *MockEnvConfig) TracingEnabled() bool { return true } -func (m *MockEnvConfig) TracingCollectorTarget() string { return "http://localhost:9000" } -func (m *MockEnvConfig) TracingSamplingRatio() float64 { return 0.1 } -func (m *MockEnvConfig) TracingAttributes() map[string]string { return map[string]string{"key": "value"} } - -func TestSetCmdEnvFromConfig(t *testing.T) { - mockConfig := &MockEnvConfig{} - cmd := exec.Command("ls") // Dummy command - SetCmdEnvFromConfig(cmd, mockConfig) - - envMap := make(map[string]string) - for _, e := range cmd.Env { - pair := splitEnv(e) - if pair != nil { - envMap[pair[0]] = pair[1] - } - } - - assert.Equal(t, strconv.Itoa(9090), envMap["CL_PROMETHEUS_PORT"]) - assert.Equal(t, "true", envMap["TRACING_ENABLED"]) - assert.Equal(t, "http://localhost:9000", envMap["TRACING_COLLECTOR_TARGET"]) - assert.Equal(t, "0.1", envMap["TRACING_SAMPLING_RATIO"]) - assert.Equal(t, "value", envMap["TRACING_ATTRIBUTE_key"]) -} - -// Helper function to split environment variables into key-value pairs -func splitEnv(env string) []string { - for i := 0; i < len(env); i++ { - if env[i] == '=' { - return []string{env[:i], env[i+1:]} - } - } - return nil -} diff --git a/plugins/loop_registry.go b/plugins/loop_registry.go index 5dc3bf69920..f402fc6fa17 100644 --- a/plugins/loop_registry.go +++ b/plugins/loop_registry.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/smartcontractkit/chainlink-relay/pkg/logger" + "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/config" ) @@ -18,7 +19,7 @@ var ErrExists = errors.New("plugin already registered") type RegisteredLoop struct { Name string - EnvCfg EnvConfig + EnvCfg loop.EnvConfig } // LoopRegistry is responsible for assigning ports to plugins that are to be used for the @@ -49,19 +50,17 @@ func (m *LoopRegistry) Register(id string) (*RegisteredLoop, error) { return nil, ErrExists } nextPort := pluginDefaultPort + len(m.registry) - envCfg := &envConfig{ - prometheusPort: nextPort, - } + envCfg := loop.EnvConfig{PrometheusPort: nextPort} if m.cfgTracing != nil { - envCfg.tracingEnabled = m.cfgTracing.Enabled() - envCfg.tracingCollectorTarget = m.cfgTracing.CollectorTarget() - envCfg.tracingAttributes = m.cfgTracing.Attributes() - envCfg.tracingSamplingRatio = m.cfgTracing.SamplingRatio() + envCfg.TracingEnabled = m.cfgTracing.Enabled() + envCfg.TracingCollectorTarget = m.cfgTracing.CollectorTarget() + envCfg.TracingAttributes = m.cfgTracing.Attributes() + envCfg.TracingSamplingRatio = m.cfgTracing.SamplingRatio() } m.registry[id] = &RegisteredLoop{Name: id, EnvCfg: envCfg} - m.lggr.Debugf("Registered loopp %q with config %v, port %d", id, envCfg, envCfg.PrometheusPort()) + m.lggr.Debugf("Registered loopp %q with config %v, port %d", id, envCfg, envCfg.PrometheusPort) return m.registry[id], nil } diff --git a/plugins/loop_registry_test.go b/plugins/loop_registry_test.go index 81d316e6469..b5da9154b68 100644 --- a/plugins/loop_registry_test.go +++ b/plugins/loop_registry_test.go @@ -14,7 +14,7 @@ func TestPluginPortManager(t *testing.T) { pFoo, err := m.Register("foo") require.NoError(t, err) require.Equal(t, "foo", pFoo.Name) - require.Greater(t, pFoo.EnvCfg.PrometheusPort(), 0) + require.Greater(t, pFoo.EnvCfg.PrometheusPort, 0) // test duplicate pNil, err := m.Register("foo") require.ErrorIs(t, err, ErrExists) @@ -23,17 +23,19 @@ func TestPluginPortManager(t *testing.T) { pBar, err := m.Register("bar") require.NoError(t, err) require.Equal(t, "bar", pBar.Name) - require.Equal(t, pFoo.EnvCfg.PrometheusPort()+1, pBar.EnvCfg.PrometheusPort()) + require.Equal(t, pFoo.EnvCfg.PrometheusPort+1, pBar.EnvCfg.PrometheusPort) } // Mock tracing config type MockCfgTracing struct{} -func (m *MockCfgTracing) Enabled() bool { return true } +func (m *MockCfgTracing) Enabled() bool { return true } func (m *MockCfgTracing) CollectorTarget() string { return "http://localhost:9000" } -func (m *MockCfgTracing) Attributes() map[string]string { return map[string]string{"attribute": "value"} } +func (m *MockCfgTracing) Attributes() map[string]string { + return map[string]string{"attribute": "value"} +} func (m *MockCfgTracing) SamplingRatio() float64 { return 0.1 } -func (m *MockCfgTracing) NodeID() string { return "" } +func (m *MockCfgTracing) NodeID() string { return "" } func TestLoopRegistry_Register(t *testing.T) { mockCfgTracing := &MockCfgTracing{} @@ -41,7 +43,7 @@ func TestLoopRegistry_Register(t *testing.T) { // Create a LoopRegistry instance with mockCfgTracing loopRegistry := &LoopRegistry{ - lggr: logger.TestLogger(t), + lggr: logger.TestLogger(t), registry: registry, cfgTracing: mockCfgTracing, } @@ -50,8 +52,8 @@ func TestLoopRegistry_Register(t *testing.T) { registeredLoop, err := loopRegistry.Register("testID") require.Nil(t, err) require.Equal(t, "testID", registeredLoop.Name) - require.True(t, registeredLoop.EnvCfg.TracingEnabled()) - require.Equal(t, "http://localhost:9000", registeredLoop.EnvCfg.TracingCollectorTarget()) - require.Equal(t, map[string]string{"attribute": "value"}, registeredLoop.EnvCfg.TracingAttributes()) - require.Equal(t, 0.1, registeredLoop.EnvCfg.TracingSamplingRatio()) -} \ No newline at end of file + require.True(t, registeredLoop.EnvCfg.TracingEnabled) + require.Equal(t, "http://localhost:9000", registeredLoop.EnvCfg.TracingCollectorTarget) + require.Equal(t, map[string]string{"attribute": "value"}, registeredLoop.EnvCfg.TracingAttributes) + require.Equal(t, 0.1, registeredLoop.EnvCfg.TracingSamplingRatio) +} diff --git a/plugins/plugin.go b/plugins/plugin.go deleted file mode 100644 index 06a59224249..00000000000 --- a/plugins/plugin.go +++ /dev/null @@ -1,41 +0,0 @@ -package plugins - -import ( - "sync" - - "github.com/smartcontractkit/chainlink-relay/pkg/logger" - "github.com/smartcontractkit/chainlink-relay/pkg/services" -) - -// Base is a base layer for plugins to easily manage sub-[types.Service]s. -type Base struct { - Logger logger.Logger - - mu sync.RWMutex - srvs []services.Service -} - -func (p *Base) Ready() error { return nil } -func (p *Base) Name() string { return p.Logger.Name() } - -func (p *Base) SubService(s services.Service) { - p.mu.Lock() - p.srvs = append(p.srvs, s) - p.mu.Unlock() -} - -func (p *Base) HealthReport() map[string]error { - hr := map[string]error{p.Name(): nil} - p.mu.RLock() - defer p.mu.RUnlock() - for _, s := range p.srvs { - services.CopyHealth(hr, s.HealthReport()) - } - return hr -} - -func (p *Base) Close() (err error) { - p.mu.RLock() - defer p.mu.RUnlock() - return services.MultiCloser(p.srvs).Close() -} diff --git a/plugins/prom.go b/plugins/prom.go deleted file mode 100644 index cf7fdf0e41b..00000000000 --- a/plugins/prom.go +++ /dev/null @@ -1,124 +0,0 @@ -package plugins - -import ( - "errors" - "fmt" - "net" - "net/http" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - "golang.org/x/net/context" - - "github.com/smartcontractkit/chainlink-relay/pkg/logger" -) - -type PromServer struct { - port int - srvrDone chan struct{} // closed when the http server is done - srvr *http.Server - tcpListener *net.TCPListener - lggr logger.Logger - - handler http.Handler -} - -type PromServerOpt func(*PromServer) - -func WithHandler(h http.Handler) PromServerOpt { - return func(s *PromServer) { - s.handler = h - } -} - -func NewPromServer(port int, lggr logger.Logger, opts ...PromServerOpt) *PromServer { - - s := &PromServer{ - port: port, - lggr: lggr, - srvrDone: make(chan struct{}), - srvr: &http.Server{ - // reasonable default based on typical prom poll interval of 15s. - ReadTimeout: 5 * time.Second, - }, - - handler: promhttp.HandlerFor( - prometheus.DefaultGatherer, - promhttp.HandlerOpts{ - EnableOpenMetrics: true, - }, - ), - } - - for _, opt := range opts { - opt(s) - } - - return s -} - -// Start start HTTP server on specified port to handle metrics requests -func (p *PromServer) Start() error { - p.lggr.Debugf("Starting prom server on port %d", p.port) - err := p.setupListener() - if err != nil { - return err - } - - http.Handle("/metrics", p.handler) - - go func() { - defer close(p.srvrDone) - err := p.srvr.Serve(p.tcpListener) - if errors.Is(err, net.ErrClosed) { - // ErrClose is expected on gracefully shutdown - p.lggr.Warnf("%s closed", p.Name()) - } else { - p.lggr.Errorf("%s: %s", p.Name(), err) - } - - }() - return nil -} - -// Close shutdowns down the underlying HTTP server. See [http.Server.Close] for details -func (p *PromServer) Close() error { - err := p.srvr.Shutdown(context.Background()) - <-p.srvrDone - return err -} - -// Name of the server -func (p *PromServer) Name() string { - return fmt.Sprintf("%s-prom-server", p.lggr.Name()) -} - -// Port is the resolved port and is only known after Start(). -// returns -1 before it is resolved or if there was an error during resolution. -func (p *PromServer) Port() int { - if p.tcpListener == nil { - return -1 - } - // always safe to cast because we explicitly have a tcp listener - // there is direct access to Port without the addr casting - // Note: addr `:0` is not resolved to non-zero port until ListenTCP is called - // net.ResolveTCPAddr sounds promising, but doesn't work in practice - return p.tcpListener.Addr().(*net.TCPAddr).Port - -} - -// setupListener creates explicit listener so that we can resolve `:0` port, which is needed for testing -// if we didn't need the resolved addr, or could pick a static port we could use p.srvr.ListenAndServer -func (p *PromServer) setupListener() error { - - l, err := net.ListenTCP("tcp", &net.TCPAddr{ - Port: p.port, - }) - if err != nil { - return err - } - - p.tcpListener = l - return nil -} diff --git a/plugins/prom_test.go b/plugins/prom_test.go deleted file mode 100644 index 39ecca6931b..00000000000 --- a/plugins/prom_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package plugins - -import ( - "fmt" - "io" - "net/http" - "testing" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-relay/pkg/logger" -) - -func TestPromServer(t *testing.T) { - - testReg := prometheus.NewRegistry() - testHandler := promhttp.HandlerFor(testReg, promhttp.HandlerOpts{}) - testMetric := prometheus.NewCounter(prometheus.CounterOpts{ - Name: "test_metric", - }) - testReg.MustRegister(testMetric) - testMetric.Inc() - - s := NewPromServer(0, logger.Test(t), WithHandler(testHandler)) - // check that port is not resolved yet - require.Equal(t, -1, s.Port()) - require.NoError(t, s.Start()) - - url := fmt.Sprintf("http://localhost:%d/metrics", s.Port()) - resp, err := http.Get(url) //nolint - require.NoError(t, err) - require.NoError(t, err, "endpoint %s", url) - require.NotNil(t, resp.Body) - b, err := io.ReadAll(resp.Body) - require.NoError(t, err) - require.Contains(t, string(b), "test_metric") - defer resp.Body.Close() - - require.NoError(t, s.Close()) -} diff --git a/plugins/server.go b/plugins/server.go deleted file mode 100644 index 77455d5bc63..00000000000 --- a/plugins/server.go +++ /dev/null @@ -1,112 +0,0 @@ -package plugins - -import ( - "fmt" - "os" - - "github.com/smartcontractkit/chainlink-relay/pkg/logger" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink/v2/core/services" -) - -// NewStartedServer returns a started Server. -// The caller is responsible for calling Server.Stop(). -func NewStartedServer(loggerName string) (*Server, error) { - s, err := newServer(loggerName) - if err != nil { - return nil, err - } - err = s.start() - if err != nil { - return nil, err - } - - return s, nil -} - -// MustNewStartedServer returns a new started Server like NewStartedServer, but logs and exits in the event of error. -// The caller is responsible for calling Server.Stop(). -func MustNewStartedServer(loggerName string) *Server { - s, err := newServer(loggerName) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to start server: %s\n", err) - os.Exit(1) - } - err = s.start() - if err != nil { - s.Logger.Fatalf("Failed to start server: %s", err) - } - - return s -} - -// Server holds common plugin server fields. -type Server struct { - loop.GRPCOpts - Logger logger.SugaredLogger - *PromServer - services.Checker -} - -func newServer(loggerName string) (*Server, error) { - s := &Server{ - // default prometheus.Registerer - GRPCOpts: loop.NewGRPCOpts(nil), - } - - lggr, err := loop.NewLogger() - if err != nil { - return nil, fmt.Errorf("error creating logger: %s", err) - } - lggr = logger.Named(lggr, loggerName) - s.Logger = logger.Sugared(lggr) - return s, nil -} - -func (s *Server) start() error { - envCfg, err := GetEnvConfig() - if err != nil { - return fmt.Errorf("error getting environment configuration: %w", err) - } - - err = loop.SetupTracing(loop.TracingConfig{ - Enabled: envCfg.TracingEnabled(), - CollectorTarget: envCfg.TracingCollectorTarget(), - NodeAttributes: envCfg.TracingAttributes(), - SamplingRatio: envCfg.TracingSamplingRatio(), - }) - if err != nil { - // non blocking to server start - s.Logger.Errorf("Failed to setup tracing: %s", err) - } - - s.PromServer = NewPromServer(envCfg.PrometheusPort(), s.Logger) - err = s.PromServer.Start() - if err != nil { - return fmt.Errorf("error starting prometheus server: %w", err) - } - - s.Checker = services.NewChecker() - err = s.Checker.Start() - if err != nil { - return fmt.Errorf("error starting health checker: %w", err) - } - - return nil -} - -// MustRegister registers the Checkable with services.Checker, or exits upon failure. -func (s *Server) MustRegister(c services.Checkable) { - if err := s.Register(c); err != nil { - s.Logger.Fatalf("Failed to register %s with health checker: %v", c.Name(), err) - } -} - -// Stop closes resources and flushes logs. -func (s *Server) Stop() { - s.Logger.ErrorIfFn(s.Checker.Close, "Failed to close health checker") - s.Logger.ErrorIfFn(s.PromServer.Close, "Failed to close prometheus server") - if err := s.Logger.Sync(); err != nil { - fmt.Println("Failed to sync logger:", err) - } -} diff --git a/plugins/utils.go b/plugins/utils.go index fb251a96078..5e5e4142e86 100644 --- a/plugins/utils.go +++ b/plugins/utils.go @@ -19,7 +19,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 - SetCmdEnvFromConfig(cmd, registeredLoop.EnvCfg) + cmd.Env = append(cmd.Env, registeredLoop.EnvCfg.AsCmdEnv()...) return cmd }, nil }