Skip to content

Commit 984c3e8

Browse files
authored
Custom port exposure, API credentials override in outputs (#1339)
custom ports, api creds in outputs, maturity model update
1 parent ed6103e commit 984c3e8

File tree

15 files changed

+149
-58
lines changed

15 files changed

+149
-58
lines changed

book/src/SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
- [CLI](./framework/cli.md)
1515
- [Configuration](./framework/configuration.md)
1616
- [Test Configuration](./framework/test_configuration_overrides.md)
17-
- [Components Persistence](framework/components/state.md)
17+
- [Exposing Components](framework/components/state.md)
1818
- [Components Cleanup](framework/components/cleanup.md)
1919
- [Components Caching](framework/components/caching.md)
2020
- [External Environment](framework/components/external.md)

book/src/framework/components/chainlink/node.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ Here we provide full configuration reference, if you want to copy and run it, pl
2121
pull_image = true
2222

2323
[cl_node.node]
24+
# custom ports that plugins may need to expose and map to the host machine
25+
custom_ports = [14000, 14001]
2426
# A list of paths to capability binaries
2527
capabilities = ["./capability_1", "./capability_2"]
2628
# Default capabilities directory inside container
@@ -54,6 +56,10 @@ Here we provide full configuration reference, if you want to copy and run it, pl
5456
use_cache = true
5557
# Describes deployed or external Chainlink node
5658
[cl_node.out.node]
59+
# API user name
60+
api_auth_user = '[email protected]'
61+
# API password
62+
api_auth_password = 'fj293fbBnlQ!f9vNs'
5763
# Host Docker URLs the test uses
5864
# in case of using external component you can replace these URLs with another deployment
5965
p2p_url = "http://127.0.0.1:32812"

book/src/framework/components/chainlink/nodeset.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ Then configure NodeSet
6868
volume_name = ""
6969

7070
[nodeset.node_specs.node]
71+
# custom ports that plugins may need to expose and map to the host machine
72+
custom_ports = [14000, 14001]
7173
# A list of paths to capability binaries
7274
capabilities = ["./capability_1", "./capability_2"]
7375
# Default capabilities directory inside container
@@ -106,6 +108,10 @@ Then configure NodeSet
106108

107109
# Describes deployed or external Chainlink node
108110
[nodeset.out.cl_nodes.node]
111+
# API user name
112+
api_auth_user = '[email protected]'
113+
# API password
114+
api_auth_password = 'fj293fbBnlQ!f9vNs'
109115
# Host Docker URLs the test uses
110116
# in case of using external component you can replace these URLs with another deployment
111117
p2p_url = "http://127.0.0.1:32996"

book/src/framework/components/external.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ For example, to integrate with remote `k8s` environment you can use `CTF_CONFIGS
4545
use_cache = true
4646

4747
[nodeset.out.cl_nodes.node]
48+
# set up your user/password for API authorization
49+
api_auth_user = '[email protected]'
50+
api_auth_password = 'fj293fbBnlQ!f9vNs'
4851
# set up each node URLs
4952
p2p_url = "http://127.0.0.1:12000"
5053
url = "http://127.0.0.1:10000"

book/src/framework/components/state.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Components Persistence
1+
# Exposing Components (Data and ports)
22

33
We use static port ranges and volumes for all components to simplify Docker port management for developers.
44

@@ -27,6 +27,7 @@ Defaults are:
2727

2828
When you run `ctf d rm` database volume will be **removed**.
2929

30+
3031
<div class="warning">
3132

3233
One node set is enough for any kind of testing, if you need more nodes consider extending your existing node set:
@@ -35,3 +36,24 @@ One node set is enough for any kind of testing, if you need more nodes consider
3536
nodes = 10
3637
```
3738
</div>
39+
40+
## Custom ports
41+
42+
You can also define a custom set of ports for any node
43+
```toml
44+
[nodeset]
45+
nodes = 5
46+
override_mode = "each"
47+
48+
[[nodeset.node_specs]]
49+
50+
[nodeset.node_specs.db]
51+
image = "postgres:15.6"
52+
pull_image = true
53+
54+
[nodeset.node_specs.node]
55+
# here we defined 2 new ports to listen and mapped them to our host machine
56+
custom_ports = [14000, 14001]
57+
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
58+
pull_image = false
59+
```

book/src/framework/test_configuration_overrides.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,14 @@ This helps reduce duplication in the configuration.
6868
6969
> [!WARNING]
7070
> When override slices remember that you should replace the full slice, it won't be extended by default!
71+
72+
## Overriding Components Configuration
73+
74+
The same override logic applies across components, files, and configuration fields in code, configs are applied in order:
75+
76+
1. Implicit component defaults that are defined inside application
77+
2. Component defaults defined in the framework or external component, ex.: [CLNode](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/framework/components/clnode/default.go)
78+
3. `Test.*Override`
79+
4. `user_.*_overrides`
80+
81+
Use `Test.*Override` in test `code` to override component configurations, and `user_.*_overrides` in `TOML` for the same purpose.

framework/.changeset/v0.2.4.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- Add custom ports exposure in TOML
2+
- Return default user/password credentials in outputs, so we can connect to any node by overriding TOML params
3+
- Change `clclient.New` signature

framework/clclient/client.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414

1515
"github.com/ethereum/go-ethereum/common"
1616
"github.com/go-resty/resty/v2"
17-
"github.com/rs/zerolog"
1817
"github.com/rs/zerolog/log"
1918
"golang.org/x/sync/errgroup"
2019
)
@@ -57,14 +56,13 @@ func NewChainlinkClient(c *Config) (*ChainlinkClient, error) {
5756
}, nil
5857
}
5958

60-
// NewCLDefaultClients connects all the clients using clnode.Output and default login/password
61-
func NewCLDefaultClients(outs []*clnode.Output, l zerolog.Logger) ([]*ChainlinkClient, error) {
59+
func New(outs []*clnode.Output) ([]*ChainlinkClient, error) {
6260
clients := make([]*ChainlinkClient, 0)
6361
for _, out := range outs {
6462
c, err := NewChainlinkClient(&Config{
6563
URL: out.Node.HostURL,
66-
Email: clnode.DefaultAPIUser,
67-
Password: clnode.DefaultAPIPassword,
64+
Email: out.Node.APIAuthUser,
65+
Password: out.Node.APIAuthPassword,
6866
})
6967
if err != nil {
7068
return nil, err

framework/components/clnode/clnode.go

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type NodeInput struct {
5151
UserSecretsOverrides string `toml:"user_secrets_overrides"`
5252
HTTPPort int `toml:"port"`
5353
P2PPort int `toml:"p2p_port"`
54+
CustomPorts []int `toml:"custom_ports"`
5455
}
5556

5657
// Output represents Chainlink node output, nodes and databases connection URLs
@@ -62,10 +63,12 @@ type Output struct {
6263

6364
// NodeOut is CL node container output, URLs to connect
6465
type NodeOut struct {
65-
HostURL string `toml:"url"`
66-
HostP2PURL string `toml:"p2p_url"`
67-
DockerURL string `toml:"docker_internal_url"`
68-
DockerP2PUrl string `toml:"p2p_docker_internal_url"`
66+
APIAuthUser string `toml:"api_auth_user"`
67+
APIAuthPassword string `toml:"api_auth_password"`
68+
HostURL string `toml:"url"`
69+
HostP2PURL string `toml:"p2p_url"`
70+
DockerURL string `toml:"docker_internal_url"`
71+
DockerP2PUrl string `toml:"p2p_docker_internal_url"`
6972
}
7073

7174
// NewNodeWithDB create a new Chainlink node with some image:tag and one or several configs
@@ -152,6 +155,35 @@ func newNode(in *Input, pgOut *postgres.Output) (*NodeOut, error) {
152155
} else {
153156
containerName = framework.DefaultTCName("node")
154157
}
158+
customPorts := make([]string, 0)
159+
for _, p := range in.Node.CustomPorts {
160+
customPorts = append(customPorts, fmt.Sprintf("%d/tcp", p))
161+
}
162+
exposedPorts := []string{httpPort, p2pPort}
163+
exposedPorts = append(exposedPorts, customPorts...)
164+
165+
portBindings := nat.PortMap{
166+
nat.Port(httpPort): []nat.PortBinding{
167+
{
168+
HostIP: "0.0.0.0",
169+
HostPort: fmt.Sprintf("%d/tcp", in.Node.HTTPPort),
170+
},
171+
},
172+
nat.Port(p2pPort): []nat.PortBinding{
173+
{
174+
HostIP: "0.0.0.0",
175+
HostPort: fmt.Sprintf("%d/udp", in.Node.P2PPort),
176+
},
177+
},
178+
}
179+
for _, p := range customPorts {
180+
portBindings[nat.Port(p)] = []nat.PortBinding{
181+
{
182+
HostIP: "0.0.0.0",
183+
HostPort: p,
184+
},
185+
}
186+
}
155187

156188
req := tc.ContainerRequest{
157189
AlwaysPullImage: in.Node.PullImage,
@@ -162,7 +194,7 @@ func newNode(in *Input, pgOut *postgres.Output) (*NodeOut, error) {
162194
NetworkAliases: map[string][]string{
163195
framework.DefaultNetworkName: {containerName},
164196
},
165-
ExposedPorts: []string{httpPort, p2pPort},
197+
ExposedPorts: exposedPorts,
166198
Entrypoint: []string{
167199
"/bin/sh", "-c",
168200
"chainlink -c /config/config -c /config/overrides -c /config/user-overrides -s /config/secrets -s /config/secrets-overrides -s /config/user-secrets-overrides node start -d -p /config/node_password -a /config/apicredentials",
@@ -171,20 +203,7 @@ func newNode(in *Input, pgOut *postgres.Output) (*NodeOut, error) {
171203
}
172204
if in.Node.HTTPPort != 0 && in.Node.P2PPort != 0 {
173205
req.HostConfigModifier = func(h *container.HostConfig) {
174-
h.PortBindings = nat.PortMap{
175-
nat.Port(httpPort): []nat.PortBinding{
176-
{
177-
HostIP: "0.0.0.0",
178-
HostPort: fmt.Sprintf("%d/tcp", in.Node.HTTPPort),
179-
},
180-
},
181-
nat.Port(p2pPort): []nat.PortBinding{
182-
{
183-
HostIP: "0.0.0.0",
184-
HostPort: fmt.Sprintf("%d/udp", in.Node.P2PPort),
185-
},
186-
},
187-
}
206+
h.PortBindings = portBindings
188207
}
189208
}
190209
files := []tc.ContainerFile{
@@ -268,10 +287,12 @@ func newNode(in *Input, pgOut *postgres.Output) (*NodeOut, error) {
268287
mpP2P := nat.Port(fmt.Sprintf("%d/udp", in.Node.P2PPort))
269288

270289
return &NodeOut{
271-
HostURL: fmt.Sprintf("http://%s:%s", host, mp.Port()),
272-
HostP2PURL: fmt.Sprintf("http://%s:%s", host, mpP2P.Port()),
273-
DockerURL: fmt.Sprintf("http://%s:%s", containerName, DefaultHTTPPort),
274-
DockerP2PUrl: fmt.Sprintf("http://%s:%s", containerName, DefaultP2PPort),
290+
APIAuthUser: DefaultAPIUser,
291+
APIAuthPassword: DefaultAPIPassword,
292+
HostURL: fmt.Sprintf("http://%s:%s", host, mp.Port()),
293+
HostP2PURL: fmt.Sprintf("http://%s:%s", host, mpP2P.Port()),
294+
DockerURL: fmt.Sprintf("http://%s:%s", containerName, DefaultHTTPPort),
295+
DockerP2PUrl: fmt.Sprintf("http://%s:%s", containerName, DefaultP2PPort),
275296
}, nil
276297
}
277298

framework/components/simple_node_set/node_set.go

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,9 @@ func NewSharedDBNodeSet(in *Input, bcOut *blockchain.Output, fakeUrl string) (*O
5050
if len(in.NodeSpecs) != in.Nodes && in.OverrideMode == "each" {
5151
return nil, fmt.Errorf("amount of 'nodes' must be equal to specs provided in override_mode='each'")
5252
}
53-
switch in.OverrideMode {
54-
case "all":
55-
out, err = sharedDBSetup(in, bcOut, fakeUrl, false)
56-
if err != nil {
57-
return nil, err
58-
}
59-
case "each":
60-
out, err = sharedDBSetup(in, bcOut, fakeUrl, true)
61-
if err != nil {
62-
return nil, err
63-
}
53+
out, err = sharedDBSetup(in, bcOut, fakeUrl)
54+
if err != nil {
55+
return nil, err
6456
}
6557
return out, nil
6658
}
@@ -80,7 +72,7 @@ func printURLs(out *Output) {
8072
framework.L.Debug().Any("DB", pgURLs).Send()
8173
}
8274

83-
func sharedDBSetup(in *Input, bcOut *blockchain.Output, fakeUrl string, overrideEach bool) (*Output, error) {
75+
func sharedDBSetup(in *Input, bcOut *blockchain.Output, fakeUrl string) (*Output, error) {
8476
in.NodeSpecs[0].DbInput.Databases = in.Nodes
8577
dbOut, err := postgres.NewPostgreSQL(in.NodeSpecs[0].DbInput)
8678
if err != nil {
@@ -107,16 +99,21 @@ func sharedDBSetup(in *Input, bcOut *blockchain.Output, fakeUrl string, override
10799
i := i
108100
var overrideIdx int
109101
var nodeName string
110-
if overrideEach {
102+
switch in.OverrideMode {
103+
case "each":
111104
overrideIdx = i
112-
} else {
105+
case "all":
113106
overrideIdx = 0
107+
if len(in.NodeSpecs[overrideIdx].Node.CustomPorts) > 0 {
108+
return nil, fmt.Errorf("custom_ports can be used only with override_mode = 'each'")
109+
}
114110
}
115111
if in.NodeSpecs[overrideIdx].Node.Name == "" {
116112
nodeName = fmt.Sprintf("node%d", i)
117113
}
118114
eg.Go(func() error {
119-
net, err := clnode.NewNetworkCfgOneNetworkAllNodes(bcOut)
115+
var net string
116+
net, err = clnode.NewNetworkCfgOneNetworkAllNodes(bcOut)
120117
if err != nil {
121118
return err
122119
}
@@ -127,6 +124,7 @@ func sharedDBSetup(in *Input, bcOut *blockchain.Output, fakeUrl string, override
127124
Node: &clnode.NodeInput{
128125
HTTPPort: httpPortRangeStart + i,
129126
P2PPort: p2pPortRangeStart + i,
127+
CustomPorts: in.NodeSpecs[overrideIdx].Node.CustomPorts,
130128
Image: in.NodeSpecs[overrideIdx].Node.Image,
131129
Name: nodeName,
132130
PullImage: in.NodeSpecs[overrideIdx].Node.PullImage,

0 commit comments

Comments
 (0)