diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 19c973efe40..c11f114c825 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1817,6 +1817,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/suzuki-shunsuke/go-convmap v0.2.1 h1:g94CxI6ENYluXZhdEH+1WVGhMAE8nLvAmWLUCwBw6W0= +github.com/suzuki-shunsuke/go-convmap v0.2.1/go.mod h1:3XfGRbtyNBMGfXAxhROSRki6/UIlUX31Qt6DvdI6lUs= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= diff --git a/deployment/cre/jobs/operations/propose_gateway_job.go b/deployment/cre/jobs/operations/propose_gateway_job.go index 4d0f83ab064..239b6dc0f15 100644 --- a/deployment/cre/jobs/operations/propose_gateway_job.go +++ b/deployment/cre/jobs/operations/propose_gateway_job.go @@ -26,8 +26,8 @@ type ProposeGatewayJobInput struct { ServiceCentricFormatEnabled bool `yaml:"serviceCentricFormatEnabled"` DONs []DON `yaml:"dons"` Services []GatewayService `yaml:"services"` - GatewayRequestTimeoutSec int `yaml:"gatewayRequestTimeoutSec"` - AllowedPorts []int `yaml:"allowedPorts"` + GatewayRequestTimeoutSec pkg.Int `yaml:"gatewayRequestTimeoutSec"` + AllowedPorts []pkg.Int `yaml:"allowedPorts"` AllowedSchemes []string `yaml:"allowedSchemes"` AllowedIPsCIDR []string `yaml:"allowedIPsCIDR"` AuthGatewayID string `yaml:"authGatewayID"` @@ -37,7 +37,7 @@ type ProposeGatewayJobInput struct { type DON struct { Name string `yaml:"name"` - F int `yaml:"f"` + F pkg.Int `yaml:"f"` Handlers []string `yaml:"handlers"` } @@ -66,7 +66,7 @@ var ProposeGatewayJob = operations.NewOperation[ProposeGatewayJobInput, ProposeG // When ServiceCentricFormatEnabled is true, it derives the set of unique DON names from // input.Services; otherwise it uses the don-centric input.DONs list. func proposeGatewayJob(b operations.Bundle, deps ProposeGatewayJobDeps, input ProposeGatewayJobInput) (ProposeGatewayJobOutput, error) { - requestTimeoutSec := input.GatewayRequestTimeoutSec + requestTimeoutSec := int(input.GatewayRequestTimeoutSec) if requestTimeoutSec == 0 { requestTimeoutSec = defaultGatewayRequestTimeoutSec } @@ -181,7 +181,7 @@ func buildServiceCentricJob(deps ProposeGatewayJobDeps, input ProposeGatewayJobI DONs: dons, Services: services, RequestTimeoutSec: requestTimeoutSec, - AllowedPorts: input.AllowedPorts, + AllowedPorts: toIntSlice(input.AllowedPorts), AllowedSchemes: input.AllowedSchemes, AllowedIPsCIDR: input.AllowedIPsCIDR, AuthGatewayID: input.AuthGatewayID, @@ -197,7 +197,7 @@ func buildLegacyFormatJob(deps ProposeGatewayJobDeps, input ProposeGatewayJobInp } targetDONs = append(targetDONs, pkg.TargetDON{ ID: ad.Name, - F: ad.F, + F: int(ad.F), Members: members, Handlers: ad.Handlers, }) @@ -207,7 +207,7 @@ func buildLegacyFormatJob(deps ProposeGatewayJobDeps, input ProposeGatewayJobInp JobName: "CRE Gateway", TargetDONs: targetDONs, RequestTimeoutSec: requestTimeoutSec, - AllowedPorts: input.AllowedPorts, + AllowedPorts: toIntSlice(input.AllowedPorts), AllowedSchemes: input.AllowedSchemes, AllowedIPsCIDR: input.AllowedIPsCIDR, AuthGatewayID: input.AuthGatewayID, @@ -288,6 +288,14 @@ func resolveDONMembers(deps ProposeGatewayJobDeps, input ProposeGatewayJobInput, return members, f, nil } +func toIntSlice(vs []pkg.Int) []int { + out := make([]int, len(vs)) + for i, v := range vs { + out[i] = int(v) + } + return out +} + func parseSelector(sel uint64) (nodev1.ChainType, string, error) { fam, err := chainsel.GetSelectorFamily(sel) if err != nil { diff --git a/deployment/cre/jobs/operations/propose_gateway_job_test.go b/deployment/cre/jobs/operations/propose_gateway_job_test.go index f363b6a23a2..52554f060ba 100644 --- a/deployment/cre/jobs/operations/propose_gateway_job_test.go +++ b/deployment/cre/jobs/operations/propose_gateway_job_test.go @@ -185,7 +185,7 @@ func TestProposeGatewayJob(t *testing.T) { output: ProposeGatewayJobOutput{ Specs: map[string][]string{ "node_5": { - "type = 'gateway'\nschemaVersion = 1\nname = 'CRE Gateway'\nexternalJobID = 'cf8aa339-6349-5e5b-9289-5c2907711200'\nforwardingAllowed = false\n\n[gatewayConfig]\n[gatewayConfig.ConnectionManagerConfig]\nAuthChallengeLen = 10\nAuthGatewayId = 'gateway-node-0'\nAuthTimestampToleranceSec = 5\nHeartbeatIntervalSec = 20\n\n[[gatewayConfig.ShardedDONs]]\nDonName = 'workflow_1_zone-b'\nF = 0\n\n[[gatewayConfig.ShardedDONs.Shards]]\n[[gatewayConfig.ShardedDONs.Shards.Nodes]]\nAddress = '0x04'\nName = 'cl-cre-one-zone-b-0 (DON workflow_1_zone-b)'\n\n[[gatewayConfig.Services]]\nServiceName = 'workflows'\nDONs = ['workflow_1_zone-b']\n\n[[gatewayConfig.Services.Handlers]]\nName = 'http-capabilities'\nServiceName = 'workflows'\n\n[gatewayConfig.Services.Handlers.Config]\nCleanUpPeriodMs = 600000\n\n[[gatewayConfig.Services.Handlers]]\nName = 'web-api-capabilities'\n\n[gatewayConfig.Services.Handlers.Config]\nmaxAllowedMessageAgeSec = 1000\n\n[gatewayConfig.Services.Handlers.Config.NodeRateLimiter]\nglobalBurst = 10\nglobalRPS = 50\nperSenderBurst = 10\nperSenderRPS = 10\n\n[gatewayConfig.HTTPClientConfig]\nMaxResponseBytes = 50000000\nAllowedPorts = [443]\nAllowedSchemes = ['https']\nAllowedIPsCIDR = []\n\n[gatewayConfig.NodeServerConfig]\nHandshakeTimeoutMillis = 1000\nMaxRequestBytes = 100000\nPath = '/'\nPort = 5003\nReadTimeoutMillis = 1000\nRequestTimeoutMillis = 5000\nWriteTimeoutMillis = 1000\n\n[gatewayConfig.UserServerConfig]\nContentTypeHeader = 'application/jsonrpc'\nMaxRequestBytes = 100000\nPath = '/'\nPort = 5002\nReadTimeoutMillis = 5000\nRequestTimeoutMillis = 5000\nWriteTimeoutMillis = 6000\n", + "type = 'gateway'\nschemaVersion = 1\nname = 'CRE Gateway'\nexternalJobID = 'cf8aa339-6349-5e5b-9289-5c2907711200'\nforwardingAllowed = false\n\n[gatewayConfig]\n[gatewayConfig.ConnectionManagerConfig]\nAuthChallengeLen = 10\nAuthGatewayId = 'gateway-node-0'\nAuthTimestampToleranceSec = 5\nHeartbeatIntervalSec = 20\n\n[[gatewayConfig.ShardedDONs]]\nDonName = 'workflow_1_zone-b'\nF = 0\n\n[[gatewayConfig.ShardedDONs.Shards]]\n[[gatewayConfig.ShardedDONs.Shards.Nodes]]\nAddress = '0x04'\nName = 'cl-cre-one-zone-b-0 (DON workflow_1_zone-b)'\n\n[[gatewayConfig.Services]]\nServiceName = 'workflows'\nDONs = ['workflow_1_zone-b']\n\n[[gatewayConfig.Services.Handlers]]\nName = 'http-capabilities'\nServiceName = 'workflows'\n\n[gatewayConfig.Services.Handlers.Config]\nCleanUpPeriodMs = 600000\n\n[gatewayConfig.Services.Handlers.Config.NodeRateLimiter]\nglobalBurst = 100\nglobalRPS = 500\nperSenderBurst = 100\nperSenderRPS = 100\n\n[[gatewayConfig.Services.Handlers]]\nName = 'web-api-capabilities'\n\n[gatewayConfig.Services.Handlers.Config]\nmaxAllowedMessageAgeSec = 1000\n\n[gatewayConfig.Services.Handlers.Config.NodeRateLimiter]\nglobalBurst = 10\nglobalRPS = 50\nperSenderBurst = 10\nperSenderRPS = 10\n\n[gatewayConfig.HTTPClientConfig]\nMaxResponseBytes = 50000000\nAllowedPorts = [443]\nAllowedSchemes = ['https']\nAllowedIPsCIDR = []\n\n[gatewayConfig.NodeServerConfig]\nHandshakeTimeoutMillis = 1000\nMaxRequestBytes = 100000\nPath = '/'\nPort = 5003\nReadTimeoutMillis = 1000\nRequestTimeoutMillis = 5000\nWriteTimeoutMillis = 1000\n\n[gatewayConfig.UserServerConfig]\nContentTypeHeader = 'application/jsonrpc'\nMaxRequestBytes = 100000\nPath = '/'\nPort = 5002\nReadTimeoutMillis = 5000\nRequestTimeoutMillis = 5000\nWriteTimeoutMillis = 6000\n", }, }, }, @@ -201,7 +201,7 @@ func TestProposeGatewayJob(t *testing.T) { output: ProposeGatewayJobOutput{ Specs: map[string][]string{ "node_5": { - "type = 'gateway'\nschemaVersion = 1\nname = 'CRE Gateway'\nexternalJobID = 'cf8aa339-6349-5e5b-9289-5c2907711200'\nforwardingAllowed = false\n\n[gatewayConfig]\n[gatewayConfig.ConnectionManagerConfig]\nAuthChallengeLen = 10\nAuthGatewayId = 'gateway-node-0'\nAuthTimestampToleranceSec = 5\nHeartbeatIntervalSec = 20\n\n[[gatewayConfig.Dons]]\nDonId = 'workflow_1_zone-b'\nF = 1\n\n[[gatewayConfig.Dons.Handlers]]\nName = 'http-capabilities'\nServiceName = 'workflows'\n\n[gatewayConfig.Dons.Handlers.Config]\nCleanUpPeriodMs = 600000\n\n[[gatewayConfig.Dons.Handlers]]\nName = 'web-api-capabilities'\n\n[gatewayConfig.Dons.Handlers.Config]\nmaxAllowedMessageAgeSec = 1000\n\n[gatewayConfig.Dons.Handlers.Config.NodeRateLimiter]\nglobalBurst = 10\nglobalRPS = 50\nperSenderBurst = 10\nperSenderRPS = 10\n\n[[gatewayConfig.Dons.Members]]\nAddress = '0x04'\nName = 'cl-cre-one-zone-b-0 (DON workflow_1_zone-b)'\n\n[gatewayConfig.HTTPClientConfig]\nMaxResponseBytes = 50000000\nAllowedPorts = [443]\nAllowedSchemes = ['https']\nAllowedIPsCIDR = []\n\n[gatewayConfig.NodeServerConfig]\nHandshakeTimeoutMillis = 1000\nMaxRequestBytes = 100000\nPath = '/'\nPort = 5003\nReadTimeoutMillis = 1000\nRequestTimeoutMillis = 5000\nWriteTimeoutMillis = 1000\n\n[gatewayConfig.UserServerConfig]\nContentTypeHeader = 'application/jsonrpc'\nMaxRequestBytes = 100000\nPath = '/'\nPort = 5002\nReadTimeoutMillis = 5000\nRequestTimeoutMillis = 5000\nWriteTimeoutMillis = 6000\n", + "type = 'gateway'\nschemaVersion = 1\nname = 'CRE Gateway'\nexternalJobID = 'cf8aa339-6349-5e5b-9289-5c2907711200'\nforwardingAllowed = false\n\n[gatewayConfig]\n[gatewayConfig.ConnectionManagerConfig]\nAuthChallengeLen = 10\nAuthGatewayId = 'gateway-node-0'\nAuthTimestampToleranceSec = 5\nHeartbeatIntervalSec = 20\n\n[[gatewayConfig.Dons]]\nDonId = 'workflow_1_zone-b'\nF = 1\n\n[[gatewayConfig.Dons.Handlers]]\nName = 'http-capabilities'\nServiceName = 'workflows'\n\n[gatewayConfig.Dons.Handlers.Config]\nCleanUpPeriodMs = 600000\n\n[gatewayConfig.Dons.Handlers.Config.NodeRateLimiter]\nglobalBurst = 100\nglobalRPS = 500\nperSenderBurst = 100\nperSenderRPS = 100\n\n[[gatewayConfig.Dons.Handlers]]\nName = 'web-api-capabilities'\n\n[gatewayConfig.Dons.Handlers.Config]\nmaxAllowedMessageAgeSec = 1000\n\n[gatewayConfig.Dons.Handlers.Config.NodeRateLimiter]\nglobalBurst = 10\nglobalRPS = 50\nperSenderBurst = 10\nperSenderRPS = 10\n\n[[gatewayConfig.Dons.Members]]\nAddress = '0x04'\nName = 'cl-cre-one-zone-b-0 (DON workflow_1_zone-b)'\n\n[gatewayConfig.HTTPClientConfig]\nMaxResponseBytes = 50000000\nAllowedPorts = [443]\nAllowedSchemes = ['https']\nAllowedIPsCIDR = []\n\n[gatewayConfig.NodeServerConfig]\nHandshakeTimeoutMillis = 1000\nMaxRequestBytes = 100000\nPath = '/'\nPort = 5003\nReadTimeoutMillis = 1000\nRequestTimeoutMillis = 5000\nWriteTimeoutMillis = 1000\n\n[gatewayConfig.UserServerConfig]\nContentTypeHeader = 'application/jsonrpc'\nMaxRequestBytes = 100000\nPath = '/'\nPort = 5002\nReadTimeoutMillis = 5000\nRequestTimeoutMillis = 5000\nWriteTimeoutMillis = 6000\n", }, }, }, diff --git a/deployment/cre/jobs/pkg/gateway_job.go b/deployment/cre/jobs/pkg/gateway_job.go index 2315f13afa7..950ebc79b41 100644 --- a/deployment/cre/jobs/pkg/gateway_job.go +++ b/deployment/cre/jobs/pkg/gateway_job.go @@ -425,7 +425,8 @@ type nodeRateLimiterConfig struct { } type httpCapabilitiesHandlerConfig struct { - CleanUpPeriodMs int `toml:"CleanUpPeriodMs"` + CleanUpPeriodMs int `toml:"CleanUpPeriodMs"` + NodeRateLimiter nodeRateLimiterConfig `toml:"NodeRateLimiter"` } func newDefaultHTTPCapabilitiesHandler() handler { @@ -434,6 +435,12 @@ func newDefaultHTTPCapabilitiesHandler() handler { ServiceName: "workflows", Config: httpCapabilitiesHandlerConfig{ CleanUpPeriodMs: 10 * 60 * 1000, // 10 minutes + NodeRateLimiter: nodeRateLimiterConfig{ + GlobalBurst: 100, + GlobalRPS: 500, + PerSenderBurst: 100, + PerSenderRPS: 100, + }, }, } } diff --git a/deployment/cre/jobs/pkg/gateway_job_test.go b/deployment/cre/jobs/pkg/gateway_job_test.go index f296e480af7..90d438284b3 100644 --- a/deployment/cre/jobs/pkg/gateway_job_test.go +++ b/deployment/cre/jobs/pkg/gateway_job_test.go @@ -290,6 +290,12 @@ ServiceName = 'workflows' [gatewayConfig.Services.Handlers.Config] CleanUpPeriodMs = 600000 +[gatewayConfig.Services.Handlers.Config.NodeRateLimiter] +globalBurst = 100 +globalRPS = 500 +perSenderBurst = 100 +perSenderRPS = 100 + [[gatewayConfig.Services]] ServiceName = 'vault' DONs = ['workflow_2'] diff --git a/deployment/cre/jobs/pkg/nodes.go b/deployment/cre/jobs/pkg/nodes.go index d397ab54cfc..4166dadc538 100644 --- a/deployment/cre/jobs/pkg/nodes.go +++ b/deployment/cre/jobs/pkg/nodes.go @@ -2,6 +2,7 @@ package pkg import ( "context" + "errors" "fmt" cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" @@ -25,6 +26,9 @@ type FetchNodeChainConfigsResponse struct { } func FetchNodeChainConfigsFromJD(ctx context.Context, e cldf.Environment, req FetchNodesRequest) ([]FetchNodeChainConfigsResponse, error) { + if e.Offchain == nil { + return nil, errors.New("offchain client (JD) is not initialized; ensure JD_GRPC or OFFCHAIN_JD_ENDPOINTS_GRPC is set") + } resp, err := e.Offchain.ListNodes(ctx, &nodev1.ListNodesRequest{Filter: req.Filters}) if err != nil { return nil, fmt.Errorf("failed to list nodes: %w", err) diff --git a/deployment/cre/jobs/pkg/types.go b/deployment/cre/jobs/pkg/types.go index adea06101db..6fefc0f3884 100644 --- a/deployment/cre/jobs/pkg/types.go +++ b/deployment/cre/jobs/pkg/types.go @@ -29,6 +29,23 @@ type OracleFactoryConfig struct { Network string `toml:"network"` // e.g., "evm" } +// Int wraps int so that YAML fields can be populated from either a numeric +// literal or a quoted string (e.g. after environment-variable substitution). +type Int int + +func (i *Int) UnmarshalYAML(node *yaml.Node) error { + v, err := strconv.Atoi(node.Value) + if err != nil { + return err + } + *i = Int(v) + return nil +} + +func (i Int) MarshalYAML() ([]byte, error) { + return []byte(strconv.Itoa(int(i))), nil +} + type ChainSelector uint64 func (cs *ChainSelector) UnmarshalText(data []byte) error { diff --git a/deployment/cre/jobs/propose_job_spec_test.go b/deployment/cre/jobs/propose_job_spec_test.go index 6fde427d25d..37b7a97e51a 100644 --- a/deployment/cre/jobs/propose_job_spec_test.go +++ b/deployment/cre/jobs/propose_job_spec_test.go @@ -11,12 +11,14 @@ import ( "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink-common/pkg/settings/cresettings" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + cldpipelineinput "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/pipeline/input" "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/node" "github.com/smartcontractkit/chainlink/deployment/cre/jobs" @@ -1753,3 +1755,132 @@ CallLimit = 1_000`, "invalid inputs for CRE settings job spec: invalid wf abcd: }) } } + +func TestProposeJobSpec_GatewayJobYAMLConversion(t *testing.T) { + t.Parallel() + + t.Run("service-centric format", func(t *testing.T) { + t.Parallel() + + yamlSpec := ` +environment: staging +domain: cre +changesets: + - job_propose_arbitrary: + payload: + donName: gateway-don + donFilters: + - key: don_name + value: gateway-don + - key: environment + value: staging + - key: product + value: cre + jobName: test-gateway-job-svc + template: gateway + inputs: + serviceCentricFormatEnabled: true + dons: + - name: workflow-don + f: 1 + services: + - servicename: workflows + handlers: + - web-api-capabilities + - http-capabilities + dons: + - workflow-don + gatewayRequestTimeoutSec: 10 + allowedSchemes: + - https + allowedIPsCIDR: + - 10.0.0.0/8 +` + var root yaml.Node + err := yaml.Unmarshal([]byte(yamlSpec), &root) + require.NoError(t, err) + + rootMap, ok := cldpipelineinput.YamlNodeToAny(&root).(map[string]any) + require.True(t, ok) + + environment, _ := rootMap["environment"].(string) + domain, _ := rootMap["domain"].(string) + + changesetData, err := cldpipelineinput.FindChangesetInData(rootMap["changesets"], "job_propose_arbitrary", "test") + require.NoError(t, err) + + changesetMap, ok := changesetData.(map[string]any) + require.True(t, ok) + + payload, ok := changesetMap["payload"] + require.True(t, ok) + + payloadBytes, err := yaml.Marshal(payload) + require.NoError(t, err) + + var parsed jobs.ProposeJobSpecInput + err = yaml.Unmarshal(payloadBytes, &parsed) + require.NoError(t, err) + + parsed.Environment = environment + parsed.Domain = domain + + assert.Equal(t, "staging", parsed.Environment) + assert.Equal(t, "cre", parsed.Domain) + assert.Equal(t, job_types.Gateway, parsed.Template) + + var gatewayInput operations.ProposeGatewayJobInput + err = parsed.Inputs.UnmarshalTo(&gatewayInput) + require.NoError(t, err) + + assert.True(t, gatewayInput.ServiceCentricFormatEnabled) + require.Len(t, gatewayInput.DONs, 1) + assert.Equal(t, "workflow-don", gatewayInput.DONs[0].Name) + assert.Equal(t, pkg.Int(1), gatewayInput.DONs[0].F) + require.Len(t, gatewayInput.Services, 1) + assert.Equal(t, "workflows", gatewayInput.Services[0].ServiceName) + assert.Equal(t, []string{"web-api-capabilities", "http-capabilities"}, gatewayInput.Services[0].Handlers) + assert.Equal(t, []string{"workflow-don"}, gatewayInput.Services[0].DONs) + assert.Equal(t, pkg.Int(10), gatewayInput.GatewayRequestTimeoutSec) + assert.Equal(t, []string{"https"}, gatewayInput.AllowedSchemes) + assert.Equal(t, []string{"10.0.0.0/8"}, gatewayInput.AllowedIPsCIDR) + + // Build GatewayJob manually; in production member addresses are resolved via JD. + gj := pkg.GatewayJob{ + ServiceCentricFormatEnabled: true, + JobName: "CRE Gateway", + DONs: []pkg.TargetDON{ + { + ID: gatewayInput.DONs[0].Name, + F: int(gatewayInput.DONs[0].F), + Members: []pkg.TargetDONMember{ + {Address: "0xdef456", Name: "mock-node-1 (DON workflow-don)"}, + }, + }, + }, + Services: []pkg.GatewayServiceConfig{ + { + ServiceName: gatewayInput.Services[0].ServiceName, + Handlers: gatewayInput.Services[0].Handlers, + DONs: gatewayInput.Services[0].DONs, + }, + }, + RequestTimeoutSec: int(gatewayInput.GatewayRequestTimeoutSec), + AllowedSchemes: gatewayInput.AllowedSchemes, + AllowedIPsCIDR: gatewayInput.AllowedIPsCIDR, + } + + require.NoError(t, gj.Validate()) + assert.True(t, gj.ServiceCentricFormatEnabled) + assert.Equal(t, "CRE Gateway", gj.JobName) + assert.Equal(t, 10, gj.RequestTimeoutSec) + assert.Equal(t, []string{"https"}, gj.AllowedSchemes) + assert.Equal(t, []string{"10.0.0.0/8"}, gj.AllowedIPsCIDR) + require.Len(t, gj.DONs, 1) + assert.Equal(t, "workflow-don", gj.DONs[0].ID) + require.Len(t, gj.Services, 1) + assert.Equal(t, "workflows", gj.Services[0].ServiceName) + assert.Equal(t, []string{"web-api-capabilities", "http-capabilities"}, gj.Services[0].Handlers) + assert.Equal(t, []string{"workflow-don"}, gj.Services[0].DONs) + }) +} diff --git a/deployment/cre/pkg/offchain/nodes.go b/deployment/cre/pkg/offchain/nodes.go index 8b7f98eb068..3b385a29f0b 100644 --- a/deployment/cre/pkg/offchain/nodes.go +++ b/deployment/cre/pkg/offchain/nodes.go @@ -2,6 +2,7 @@ package offchain import ( "context" + "errors" "fmt" "slices" "sort" @@ -32,6 +33,9 @@ const ( ) func FetchNodesFromJD(ctx context.Context, jd cldf_offchain.Client, filter *nodeapiv1.ListNodesRequest_Filter) (nodes []*nodeapiv1.Node, err error) { + if jd == nil { + return nil, errors.New("offchain client (JD) is not initialized; ensure JD_GRPC or OFFCHAIN_JD_ENDPOINTS_GRPC is set") + } resp, err := jd.ListNodes(ctx, &nodeapiv1.ListNodesRequest{Filter: filter}) if err != nil { return nil, fmt.Errorf("failed to list nodes: %w", err) diff --git a/deployment/go.mod b/deployment/go.mod index 5b0f3e843a8..62165212e6c 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -454,6 +454,7 @@ require ( github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/supranational/blst v0.3.16 // indirect + github.com/suzuki-shunsuke/go-convmap v0.2.1 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a // indirect diff --git a/deployment/go.sum b/deployment/go.sum index 7e008873f69..87f706591c9 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1560,6 +1560,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/suzuki-shunsuke/go-convmap v0.2.1 h1:g94CxI6ENYluXZhdEH+1WVGhMAE8nLvAmWLUCwBw6W0= +github.com/suzuki-shunsuke/go-convmap v0.2.1/go.mod h1:3XfGRbtyNBMGfXAxhROSRki6/UIlUX31Qt6DvdI6lUs= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= diff --git a/system-tests/lib/go.sum b/system-tests/lib/go.sum index 37990b09e51..ec5cce345cf 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -1781,6 +1781,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/suzuki-shunsuke/go-convmap v0.2.1 h1:g94CxI6ENYluXZhdEH+1WVGhMAE8nLvAmWLUCwBw6W0= +github.com/suzuki-shunsuke/go-convmap v0.2.1/go.mod h1:3XfGRbtyNBMGfXAxhROSRki6/UIlUX31Qt6DvdI6lUs= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index 29d9a8ace38..016bb938e18 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -1977,6 +1977,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/suzuki-shunsuke/go-convmap v0.2.1 h1:g94CxI6ENYluXZhdEH+1WVGhMAE8nLvAmWLUCwBw6W0= +github.com/suzuki-shunsuke/go-convmap v0.2.1/go.mod h1:3XfGRbtyNBMGfXAxhROSRki6/UIlUX31Qt6DvdI6lUs= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=