Skip to content

Commit 5d2a8fb

Browse files
committed
YAML parsing test
1 parent cd466fd commit 5d2a8fb

12 files changed

Lines changed: 194 additions & 8 deletions

File tree

core/scripts/go.sum

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

deployment/cre/jobs/operations/propose_gateway_job.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ type ProposeGatewayJobInput struct {
2626
ServiceCentricFormatEnabled bool `yaml:"serviceCentricFormatEnabled"`
2727
DONs []DON `yaml:"dons"`
2828
Services []GatewayService `yaml:"services"`
29-
GatewayRequestTimeoutSec int `yaml:"gatewayRequestTimeoutSec"`
30-
AllowedPorts []int `yaml:"allowedPorts"`
29+
GatewayRequestTimeoutSec pkg.Int `yaml:"gatewayRequestTimeoutSec"`
30+
AllowedPorts []pkg.Int `yaml:"allowedPorts"`
3131
AllowedSchemes []string `yaml:"allowedSchemes"`
3232
AllowedIPsCIDR []string `yaml:"allowedIPsCIDR"`
3333
AuthGatewayID string `yaml:"authGatewayID"`
@@ -37,7 +37,7 @@ type ProposeGatewayJobInput struct {
3737

3838
type DON struct {
3939
Name string `yaml:"name"`
40-
F int `yaml:"f"`
40+
F pkg.Int `yaml:"f"`
4141
Handlers []string `yaml:"handlers"`
4242
}
4343

@@ -66,7 +66,7 @@ var ProposeGatewayJob = operations.NewOperation[ProposeGatewayJobInput, ProposeG
6666
// When ServiceCentricFormatEnabled is true, it derives the set of unique DON names from
6767
// input.Services; otherwise it uses the don-centric input.DONs list.
6868
func proposeGatewayJob(b operations.Bundle, deps ProposeGatewayJobDeps, input ProposeGatewayJobInput) (ProposeGatewayJobOutput, error) {
69-
requestTimeoutSec := input.GatewayRequestTimeoutSec
69+
requestTimeoutSec := int(input.GatewayRequestTimeoutSec)
7070
if requestTimeoutSec == 0 {
7171
requestTimeoutSec = defaultGatewayRequestTimeoutSec
7272
}
@@ -181,7 +181,7 @@ func buildServiceCentricJob(deps ProposeGatewayJobDeps, input ProposeGatewayJobI
181181
DONs: dons,
182182
Services: services,
183183
RequestTimeoutSec: requestTimeoutSec,
184-
AllowedPorts: input.AllowedPorts,
184+
AllowedPorts: toIntSlice(input.AllowedPorts),
185185
AllowedSchemes: input.AllowedSchemes,
186186
AllowedIPsCIDR: input.AllowedIPsCIDR,
187187
AuthGatewayID: input.AuthGatewayID,
@@ -197,7 +197,7 @@ func buildLegacyFormatJob(deps ProposeGatewayJobDeps, input ProposeGatewayJobInp
197197
}
198198
targetDONs = append(targetDONs, pkg.TargetDON{
199199
ID: ad.Name,
200-
F: ad.F,
200+
F: int(ad.F),
201201
Members: members,
202202
Handlers: ad.Handlers,
203203
})
@@ -207,7 +207,7 @@ func buildLegacyFormatJob(deps ProposeGatewayJobDeps, input ProposeGatewayJobInp
207207
JobName: "CRE Gateway",
208208
TargetDONs: targetDONs,
209209
RequestTimeoutSec: requestTimeoutSec,
210-
AllowedPorts: input.AllowedPorts,
210+
AllowedPorts: toIntSlice(input.AllowedPorts),
211211
AllowedSchemes: input.AllowedSchemes,
212212
AllowedIPsCIDR: input.AllowedIPsCIDR,
213213
AuthGatewayID: input.AuthGatewayID,
@@ -288,6 +288,14 @@ func resolveDONMembers(deps ProposeGatewayJobDeps, input ProposeGatewayJobInput,
288288
return members, f, nil
289289
}
290290

291+
func toIntSlice(vs []pkg.Int) []int {
292+
out := make([]int, len(vs))
293+
for i, v := range vs {
294+
out[i] = int(v)
295+
}
296+
return out
297+
}
298+
291299
func parseSelector(sel uint64) (nodev1.ChainType, string, error) {
292300
fam, err := chainsel.GetSelectorFamily(sel)
293301
if err != nil {

deployment/cre/jobs/pkg/gateway_job.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,8 @@ type nodeRateLimiterConfig struct {
425425
}
426426

427427
type httpCapabilitiesHandlerConfig struct {
428-
CleanUpPeriodMs int `toml:"CleanUpPeriodMs"`
428+
CleanUpPeriodMs int `toml:"CleanUpPeriodMs"`
429+
NodeRateLimiter nodeRateLimiterConfig `toml:"NodeRateLimiter"`
429430
}
430431

431432
func newDefaultHTTPCapabilitiesHandler() handler {
@@ -434,6 +435,12 @@ func newDefaultHTTPCapabilitiesHandler() handler {
434435
ServiceName: "workflows",
435436
Config: httpCapabilitiesHandlerConfig{
436437
CleanUpPeriodMs: 10 * 60 * 1000, // 10 minutes
438+
NodeRateLimiter: nodeRateLimiterConfig{
439+
GlobalBurst: 100,
440+
GlobalRPS: 500,
441+
PerSenderBurst: 100,
442+
PerSenderRPS: 100,
443+
},
437444
},
438445
}
439446
}

deployment/cre/jobs/pkg/gateway_job_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,12 @@ ServiceName = 'workflows'
290290
[gatewayConfig.Services.Handlers.Config]
291291
CleanUpPeriodMs = 600000
292292
293+
[gatewayConfig.Services.Handlers.Config.NodeRateLimiter]
294+
globalBurst = 100
295+
globalRPS = 500
296+
perSenderBurst = 100
297+
perSenderRPS = 100
298+
293299
[[gatewayConfig.Services]]
294300
ServiceName = 'vault'
295301
DONs = ['workflow_2']

deployment/cre/jobs/pkg/nodes.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package pkg
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67

78
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
@@ -25,6 +26,9 @@ type FetchNodeChainConfigsResponse struct {
2526
}
2627

2728
func FetchNodeChainConfigsFromJD(ctx context.Context, e cldf.Environment, req FetchNodesRequest) ([]FetchNodeChainConfigsResponse, error) {
29+
if e.Offchain == nil {
30+
return nil, errors.New("offchain client (JD) is not initialized; ensure JD_GRPC or OFFCHAIN_JD_ENDPOINTS_GRPC is set")
31+
}
2832
resp, err := e.Offchain.ListNodes(ctx, &nodev1.ListNodesRequest{Filter: req.Filters})
2933
if err != nil {
3034
return nil, fmt.Errorf("failed to list nodes: %w", err)

deployment/cre/jobs/pkg/types.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,23 @@ type OracleFactoryConfig struct {
2929
Network string `toml:"network"` // e.g., "evm"
3030
}
3131

32+
// Int wraps int so that YAML fields can be populated from either a numeric
33+
// literal or a quoted string (e.g. after environment-variable substitution).
34+
type Int int
35+
36+
func (i *Int) UnmarshalYAML(node *yaml.Node) error {
37+
v, err := strconv.Atoi(node.Value)
38+
if err != nil {
39+
return err
40+
}
41+
*i = Int(v)
42+
return nil
43+
}
44+
45+
func (i Int) MarshalYAML() ([]byte, error) {
46+
return []byte(strconv.Itoa(int(i))), nil
47+
}
48+
3249
type ChainSelector uint64
3350

3451
func (cs *ChainSelector) UnmarshalText(data []byte) error {

deployment/cre/jobs/propose_job_spec_test.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import (
1111
"github.com/pelletier/go-toml/v2"
1212
"github.com/stretchr/testify/assert"
1313
"github.com/stretchr/testify/require"
14+
"gopkg.in/yaml.v3"
1415

1516
chainsel "github.com/smartcontractkit/chain-selectors"
1617

1718
"github.com/smartcontractkit/chainlink-common/pkg/settings/cresettings"
1819
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"
1920
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
21+
cldpipelineinput "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/pipeline/input"
2022
"github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job"
2123
"github.com/smartcontractkit/chainlink-protos/job-distributor/v1/node"
2224
"github.com/smartcontractkit/chainlink/deployment/cre/jobs"
@@ -1753,3 +1755,132 @@ CallLimit = 1_000`, "invalid inputs for CRE settings job spec: invalid wf abcd:
17531755
})
17541756
}
17551757
}
1758+
1759+
func TestProposeJobSpec_GatewayJobYAMLConversion(t *testing.T) {
1760+
t.Parallel()
1761+
1762+
t.Run("service-centric format", func(t *testing.T) {
1763+
t.Parallel()
1764+
1765+
yamlSpec := `
1766+
environment: staging
1767+
domain: cre
1768+
changesets:
1769+
- job_propose_arbitrary:
1770+
payload:
1771+
donName: gateway-don
1772+
donFilters:
1773+
- key: don_name
1774+
value: gateway-don
1775+
- key: environment
1776+
value: staging
1777+
- key: product
1778+
value: cre
1779+
jobName: test-gateway-job-svc
1780+
template: gateway
1781+
inputs:
1782+
serviceCentricFormatEnabled: true
1783+
dons:
1784+
- name: workflow-don
1785+
f: 1
1786+
services:
1787+
- servicename: workflows
1788+
handlers:
1789+
- web-api-capabilities
1790+
- http-capabilities
1791+
dons:
1792+
- workflow-don
1793+
gatewayRequestTimeoutSec: 10
1794+
allowedSchemes:
1795+
- https
1796+
allowedIPsCIDR:
1797+
- 10.0.0.0/8
1798+
`
1799+
var root yaml.Node
1800+
err := yaml.Unmarshal([]byte(yamlSpec), &root)
1801+
require.NoError(t, err)
1802+
1803+
rootMap, ok := cldpipelineinput.YamlNodeToAny(&root).(map[string]any)
1804+
require.True(t, ok)
1805+
1806+
environment, _ := rootMap["environment"].(string)
1807+
domain, _ := rootMap["domain"].(string)
1808+
1809+
changesetData, err := cldpipelineinput.FindChangesetInData(rootMap["changesets"], "job_propose_arbitrary", "test")
1810+
require.NoError(t, err)
1811+
1812+
changesetMap, ok := changesetData.(map[string]any)
1813+
require.True(t, ok)
1814+
1815+
payload, ok := changesetMap["payload"]
1816+
require.True(t, ok)
1817+
1818+
payloadBytes, err := yaml.Marshal(payload)
1819+
require.NoError(t, err)
1820+
1821+
var parsed jobs.ProposeJobSpecInput
1822+
err = yaml.Unmarshal(payloadBytes, &parsed)
1823+
require.NoError(t, err)
1824+
1825+
parsed.Environment = environment
1826+
parsed.Domain = domain
1827+
1828+
assert.Equal(t, "staging", parsed.Environment)
1829+
assert.Equal(t, "cre", parsed.Domain)
1830+
assert.Equal(t, job_types.Gateway, parsed.Template)
1831+
1832+
var gatewayInput operations.ProposeGatewayJobInput
1833+
err = parsed.Inputs.UnmarshalTo(&gatewayInput)
1834+
require.NoError(t, err)
1835+
1836+
assert.True(t, gatewayInput.ServiceCentricFormatEnabled)
1837+
require.Len(t, gatewayInput.DONs, 1)
1838+
assert.Equal(t, "workflow-don", gatewayInput.DONs[0].Name)
1839+
assert.Equal(t, pkg.Int(1), gatewayInput.DONs[0].F)
1840+
require.Len(t, gatewayInput.Services, 1)
1841+
assert.Equal(t, "workflows", gatewayInput.Services[0].ServiceName)
1842+
assert.Equal(t, []string{"web-api-capabilities", "http-capabilities"}, gatewayInput.Services[0].Handlers)
1843+
assert.Equal(t, []string{"workflow-don"}, gatewayInput.Services[0].DONs)
1844+
assert.Equal(t, pkg.Int(10), gatewayInput.GatewayRequestTimeoutSec)
1845+
assert.Equal(t, []string{"https"}, gatewayInput.AllowedSchemes)
1846+
assert.Equal(t, []string{"10.0.0.0/8"}, gatewayInput.AllowedIPsCIDR)
1847+
1848+
// Build GatewayJob manually; in production member addresses are resolved via JD.
1849+
gj := pkg.GatewayJob{
1850+
ServiceCentricFormatEnabled: true,
1851+
JobName: "CRE Gateway",
1852+
DONs: []pkg.TargetDON{
1853+
{
1854+
ID: gatewayInput.DONs[0].Name,
1855+
F: int(gatewayInput.DONs[0].F),
1856+
Members: []pkg.TargetDONMember{
1857+
{Address: "0xdef456", Name: "mock-node-1 (DON workflow-don)"},
1858+
},
1859+
},
1860+
},
1861+
Services: []pkg.GatewayServiceConfig{
1862+
{
1863+
ServiceName: gatewayInput.Services[0].ServiceName,
1864+
Handlers: gatewayInput.Services[0].Handlers,
1865+
DONs: gatewayInput.Services[0].DONs,
1866+
},
1867+
},
1868+
RequestTimeoutSec: int(gatewayInput.GatewayRequestTimeoutSec),
1869+
AllowedSchemes: gatewayInput.AllowedSchemes,
1870+
AllowedIPsCIDR: gatewayInput.AllowedIPsCIDR,
1871+
}
1872+
1873+
require.NoError(t, gj.Validate())
1874+
assert.True(t, gj.ServiceCentricFormatEnabled)
1875+
assert.Equal(t, "CRE Gateway", gj.JobName)
1876+
assert.Equal(t, 10, gj.RequestTimeoutSec)
1877+
assert.Equal(t, []string{"https"}, gj.AllowedSchemes)
1878+
assert.Equal(t, []string{"10.0.0.0/8"}, gj.AllowedIPsCIDR)
1879+
require.Len(t, gj.DONs, 1)
1880+
assert.Equal(t, "workflow-don", gj.DONs[0].ID)
1881+
require.Len(t, gj.Services, 1)
1882+
assert.Equal(t, "workflows", gj.Services[0].ServiceName)
1883+
assert.Equal(t, []string{"web-api-capabilities", "http-capabilities"}, gj.Services[0].Handlers)
1884+
assert.Equal(t, []string{"workflow-don"}, gj.Services[0].DONs)
1885+
})
1886+
}

deployment/cre/pkg/offchain/nodes.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package offchain
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"slices"
78
"sort"
@@ -32,6 +33,9 @@ const (
3233
)
3334

3435
func FetchNodesFromJD(ctx context.Context, jd cldf_offchain.Client, filter *nodeapiv1.ListNodesRequest_Filter) (nodes []*nodeapiv1.Node, err error) {
36+
if jd == nil {
37+
return nil, errors.New("offchain client (JD) is not initialized; ensure JD_GRPC or OFFCHAIN_JD_ENDPOINTS_GRPC is set")
38+
}
3539
resp, err := jd.ListNodes(ctx, &nodeapiv1.ListNodesRequest{Filter: filter})
3640
if err != nil {
3741
return nil, fmt.Errorf("failed to list nodes: %w", err)

deployment/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ require (
454454
github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
455455
github.com/stretchr/objx v0.5.2 // indirect
456456
github.com/supranational/blst v0.3.16 // indirect
457+
github.com/suzuki-shunsuke/go-convmap v0.2.1 // indirect
457458
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
458459
github.com/tendermint/go-amino v0.16.0 // indirect
459460
github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a // indirect

deployment/go.sum

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)