Skip to content

Commit 283d6a5

Browse files
committed
try jd
1 parent eac78c7 commit 283d6a5

File tree

14 files changed

+669
-275
lines changed

14 files changed

+669
-275
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: Framework Golden Tests Examples (Private)
2+
on:
3+
push:
4+
5+
jobs:
6+
test:
7+
defaults:
8+
run:
9+
working-directory: framework/examples/myproject_cll
10+
env:
11+
LOKI_TENANT_ID: promtail
12+
LOKI_URL: http://localhost:3030/loki/api/v1/push
13+
# this is not the best practice, and it must be fixed, run your tests WITHOUT IT!
14+
# however, on current latest image we must use this flag
15+
CTF_IGNORE_CRITICAL_LOGS: true
16+
runs-on: ubuntu-latest
17+
permissions:
18+
id-token: write
19+
contents: read
20+
strategy:
21+
fail-fast: false
22+
matrix:
23+
test:
24+
- name: TestJD
25+
config: jd.toml
26+
count: 1
27+
timeout: 10m
28+
name: ${{ matrix.test.name }}
29+
steps:
30+
- name: Checkout repo
31+
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
32+
- name: Configure AWS credentials using OIDC
33+
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
34+
with:
35+
role-to-assume: ${{ secrets.PUBLIC_AWS_ECR_ROLE }}
36+
aws-region: us-east-1
37+
- name: Authenticate to ECR Public
38+
id: login-ecr-public
39+
uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1
40+
with:
41+
registry-type: public
42+
- name: Check for changes in Framework
43+
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
44+
id: changes
45+
with:
46+
filters: |
47+
src:
48+
- 'framework/**'
49+
- '.github/workflows/framework-golden-tests.yml'
50+
- name: Set up Go
51+
uses: actions/setup-go@v4
52+
with:
53+
go-version: 1.22.8
54+
- name: Cache Go modules
55+
uses: actions/cache@v3
56+
with:
57+
path: |
58+
~/.cache/go-build
59+
~/go/pkg/mod
60+
key: go-modules-${{ hashFiles('framework/examples/myproject_cll/go.sum') }}-${{ runner.os }}-framework-golden-examples
61+
restore-keys: |
62+
go-modules-${{ runner.os }}-framework-golden-examples
63+
go-modules-${{ runner.os }}
64+
- name: Install dependencies
65+
run: go mod download
66+
- name: Run System Tests
67+
if: steps.changes.outputs.src == 'true'
68+
env:
69+
CTF_CONFIGS: ${{ matrix.test.config }}
70+
run: |
71+
go test -timeout ${{ matrix.test.timeout }} -v -count ${{ matrix.test.count }} -run ${{ matrix.test.name }}
72+
- name: Upload Logs
73+
if: always()
74+
uses: actions/upload-artifact@v3
75+
with:
76+
name: container-logs-${{ matrix.test.name }}
77+
path: framework/examples/myproject_cll/logs
78+
retention-days: 1

book/src/framework/components/state.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ One node set is enough for any kind of testing, if you need more nodes consider
4040

4141
## Custom ports
4242

43-
You can also define a custom set of ports for any node
43+
You can also define a custom set of ports for any node.
4444
```toml
4545
[nodeset]
4646
nodes = 5
@@ -53,6 +53,7 @@ You can also define a custom set of ports for any node
5353

5454
[nodeset.node_specs.node]
5555
# here we defined 2 new ports to listen and mapped them to our host machine
56-
custom_ports = [14000, 14001]
56+
# syntax is "host:docker", if you provide only host port then we map 1-to-1
57+
custom_ports = ["14000:15000", "20000"]
5758
image = "public.ecr.aws/chainlink/chainlink:v2.16.0"
5859
```

framework/.changeset/v0.3.2.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
- Allow exposing CL node ports in host:docker format
2+
- Expose default test private keys as framework constants
3+
- Rename CHAINLINK_IMAGE to CTF_CHAINLINK_IMAGE to avoid CI collisions
4+
- Add CTF_JD_IMAGE env var
5+
- Add JobDistributor component

framework/components/clnode/clnode.go

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,54 @@ func NewNode(in *Input, pgOut *postgres.Output) (*Output, error) {
112112
return out, nil
113113
}
114114

115+
// generatePortBindings generates exposed ports and port bindings
116+
// exposes default CL node port
117+
// exposes custom_ports in format "host:docker" or map 1-to-1 if only "host" port is provided
118+
func generatePortBindings(in *Input) ([]string, nat.PortMap, error) {
119+
httpPort := fmt.Sprintf("%s/tcp", DefaultHTTPPort)
120+
portBindings := nat.PortMap{
121+
nat.Port(httpPort): []nat.PortBinding{
122+
{
123+
HostIP: "0.0.0.0",
124+
HostPort: fmt.Sprintf("%d/tcp", in.Node.HTTPPort),
125+
},
126+
},
127+
}
128+
customPorts := make([]string, 0)
129+
for _, p := range in.Node.CustomPorts {
130+
if strings.Contains(p, CustomPortSeparator) {
131+
pp := strings.Split(p, CustomPortSeparator)
132+
if len(pp) != 2 {
133+
return nil, nil, errors.New("custom_ports has ':' but you must provide both ports")
134+
}
135+
customPorts = append(customPorts, fmt.Sprintf("%s/tcp", pp[1]))
136+
137+
dockerPort := nat.Port(fmt.Sprintf("%s/tcp", pp[1]))
138+
hostPort := fmt.Sprintf("%s/tcp", pp[0])
139+
portBindings[dockerPort] = []nat.PortBinding{
140+
{
141+
HostIP: "0.0.0.0",
142+
HostPort: hostPort,
143+
},
144+
}
145+
} else {
146+
customPorts = append(customPorts, fmt.Sprintf("%s/tcp", p))
147+
148+
dockerPort := nat.Port(fmt.Sprintf("%s/tcp", p))
149+
hostPort := fmt.Sprintf("%s/tcp", p)
150+
portBindings[dockerPort] = []nat.PortBinding{
151+
{
152+
HostIP: "0.0.0.0",
153+
HostPort: hostPort,
154+
},
155+
}
156+
}
157+
}
158+
exposedPorts := []string{httpPort}
159+
exposedPorts = append(exposedPorts, customPorts...)
160+
return exposedPorts, portBindings, nil
161+
}
162+
115163
func newNode(in *Input, pgOut *postgres.Output) (*NodeOut, error) {
116164
ctx := context.Background()
117165

@@ -148,52 +196,17 @@ func newNode(in *Input, pgOut *postgres.Output) (*NodeOut, error) {
148196
return nil, err
149197
}
150198

151-
httpPort := fmt.Sprintf("%s/tcp", DefaultHTTPPort)
152199
var containerName string
153200
if in.Node.Name != "" {
154201
containerName = in.Node.Name
155202
} else {
156203
containerName = framework.DefaultTCName("node")
157204
}
158205

159-
portBindings := nat.PortMap{
160-
nat.Port(httpPort): []nat.PortBinding{
161-
{
162-
HostIP: "0.0.0.0",
163-
HostPort: fmt.Sprintf("%d/tcp", in.Node.HTTPPort),
164-
},
165-
},
166-
}
167-
customPorts := make([]string, 0)
168-
for _, p := range in.Node.CustomPorts {
169-
if strings.Contains(p, CustomPortSeparator) {
170-
pp := strings.Split(p, CustomPortSeparator)
171-
if len(pp) != 2 {
172-
return nil, errors.New("custom_ports has ':' but you must provide both ports")
173-
}
174-
customPorts = append(customPorts, fmt.Sprintf("%s/tcp", pp[1]))
175-
portBindings[nat.Port(fmt.Sprintf("%s/tcp", pp[1]))] = []nat.PortBinding{
176-
{
177-
HostIP: "0.0.0.0",
178-
HostPort: fmt.Sprintf("%s/tcp", pp[0]),
179-
},
180-
}
181-
framework.L.Warn().Str("PortHost", pp[0]).Str("PortInternal", pp[1]).Send()
182-
} else {
183-
customPorts = append(customPorts, fmt.Sprintf("%s/tcp", p))
184-
framework.L.Warn().Str("Port", p).Send()
185-
portBindings[nat.Port(fmt.Sprintf("%s/tcp", p))] = []nat.PortBinding{
186-
{
187-
HostIP: "0.0.0.0",
188-
HostPort: fmt.Sprintf("%s/tcp", p),
189-
},
190-
}
191-
}
206+
exposedPorts, portBindings, err := generatePortBindings(in)
207+
if err != nil {
208+
return nil, err
192209
}
193-
exposedPorts := []string{httpPort}
194-
exposedPorts = append(exposedPorts, customPorts...)
195-
framework.L.Warn().Any("ExposedPorts", exposedPorts).Send()
196-
197210
req := tc.ContainerRequest{
198211
AlwaysPullImage: in.Node.PullImage,
199212
Image: in.Node.Image,

framework/components/jd/jd.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package jd
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/docker/docker/api/types/container"
7+
"github.com/docker/go-connections/nat"
8+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
9+
tc "github.com/testcontainers/testcontainers-go"
10+
tcwait "github.com/testcontainers/testcontainers-go/wait"
11+
"os"
12+
)
13+
14+
const (
15+
TmpImageName = "jd-local"
16+
GRPCPort string = "42242"
17+
CSAEncryptionKey string = "!PASsword000!"
18+
WSRPCPort string = "8080"
19+
)
20+
21+
type Input struct {
22+
Image string `toml:"image"`
23+
GRPCPort string `toml:"grpc_port"`
24+
WSRPCPort string `toml:"wsrpc_port"`
25+
DBURL string `toml:"db_url"`
26+
CSAEncryptionKey string `toml:"csa_encryption_key"`
27+
DockerFilePath string `toml:"docker_file"`
28+
DockerContext string `toml:"docker_ctx"`
29+
Out *Output `toml:"out"`
30+
}
31+
32+
type Output struct {
33+
UseCache bool `toml:"use_cache"`
34+
HostGRPCUrl string `toml:"grpc_url"`
35+
DockerGRPCUrl string `toml:"docker_internal_grpc_url"`
36+
HostWSRPCUrl string `toml:"wsrpc_url"`
37+
DockerWSRPCUrl string `toml:"docker_internal_wsrpc_url"`
38+
}
39+
40+
func defaults(in *Input) {
41+
if in.GRPCPort == "" {
42+
in.GRPCPort = GRPCPort
43+
}
44+
if in.WSRPCPort == "" {
45+
in.WSRPCPort = WSRPCPort
46+
}
47+
if in.CSAEncryptionKey == "" {
48+
in.CSAEncryptionKey = CSAEncryptionKey
49+
}
50+
}
51+
52+
func NewJD(in *Input) (*Output, error) {
53+
if in.Out != nil && in.Out.UseCache {
54+
return in.Out, nil
55+
}
56+
ctx := context.Background()
57+
defaults(in)
58+
jdImg := os.Getenv("CTF_JD_IMAGE")
59+
if jdImg != "" {
60+
in.Image = jdImg
61+
}
62+
containerName := framework.DefaultTCName("jd")
63+
bindPort := fmt.Sprintf("%s/tcp", in.GRPCPort)
64+
req := tc.ContainerRequest{
65+
Name: containerName,
66+
Image: in.Image,
67+
Labels: framework.DefaultTCLabels(),
68+
Networks: []string{framework.DefaultNetworkName},
69+
NetworkAliases: map[string][]string{
70+
framework.DefaultNetworkName: {containerName},
71+
},
72+
ExposedPorts: []string{bindPort},
73+
HostConfigModifier: func(h *container.HostConfig) {
74+
h.PortBindings = framework.MapTheSamePort(bindPort)
75+
},
76+
Env: map[string]string{
77+
"DATABASE_URL": in.DBURL,
78+
"PORT": in.GRPCPort,
79+
"NODE_RPC_PORT": in.WSRPCPort,
80+
"CSA_KEY_ENCRYPTION_SECRET": in.CSAEncryptionKey,
81+
},
82+
WaitingFor: tcwait.ForAll(
83+
tcwait.ForListeningPort(nat.Port(fmt.Sprintf("%s/tcp", in.GRPCPort))),
84+
),
85+
}
86+
if req.Image == "" {
87+
req.Image = TmpImageName
88+
if err := framework.BuildImage(in.DockerContext, in.DockerFilePath, req.Image); err != nil {
89+
return nil, err
90+
}
91+
req.KeepImage = false
92+
}
93+
c, err := tc.GenericContainer(ctx, tc.GenericContainerRequest{
94+
ContainerRequest: req,
95+
Started: true,
96+
})
97+
if err != nil {
98+
return nil, err
99+
}
100+
host, err := framework.GetHost(c)
101+
if err != nil {
102+
return nil, err
103+
}
104+
out := &Output{
105+
UseCache: true,
106+
HostGRPCUrl: fmt.Sprintf("http://%s:%s", host, in.GRPCPort),
107+
DockerGRPCUrl: fmt.Sprintf("http://%s:%s", containerName, in.GRPCPort),
108+
HostWSRPCUrl: fmt.Sprintf("ws://%s:%s", host, in.WSRPCPort),
109+
DockerWSRPCUrl: fmt.Sprintf("ws://%s:%s", containerName, in.WSRPCPort),
110+
}
111+
in.Out = out
112+
return out, nil
113+
}

framework/components/jd/jd_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package jd_test
2+
3+
import (
4+
"github.com/davecgh/go-spew/spew"
5+
"github.com/smartcontractkit/chainlink-testing-framework/framework"
6+
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/jd"
7+
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/postgres"
8+
"github.com/stretchr/testify/require"
9+
"os"
10+
"sync"
11+
"testing"
12+
)
13+
14+
// here we only test that we can boot up JD
15+
// client examples are under "examples" dir
16+
// since JD is private this env var should be set locally and in CI
17+
func TestJD(t *testing.T) {
18+
err := framework.DefaultNetwork(&sync.Once{})
19+
require.NoError(t, err)
20+
pgOut, err := postgres.NewPostgreSQL(&postgres.Input{
21+
Image: "postgres:12.0",
22+
})
23+
require.NoError(t, err)
24+
out, err := jd.NewJD(&jd.Input{
25+
DBURL: pgOut.JDDockerInternalURL,
26+
Image: os.Getenv("CTF_JD_IMAGE"),
27+
})
28+
require.NoError(t, err)
29+
spew.Dump(out)
30+
}

0 commit comments

Comments
 (0)