Skip to content

Commit d1422b4

Browse files
committed
rebase
2 parents 60e2916 + 2551105 commit d1422b4

14 files changed

Lines changed: 925 additions & 269 deletions

File tree

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,47 @@
11
# Asserting Container Logs
22

3-
You can either assert that CL nodes have no errors like that, we check `(CRIT|PANIC|FATAL)` levels by default for all the nodes
3+
Use built-in critical-level assertion (`CRIT|PANIC|FATAL`) for Chainlink node logs:
44

55
```golang
6-
in, err := framework.Load[Cfg](t)
6+
in, err := framework.Load[Cfg](t)
7+
require.NoError(t, err)
8+
t.Cleanup(func() {
9+
err := framework.SaveAndCheckLogs(t)
710
require.NoError(t, err)
8-
t.Cleanup(func() {
9-
err := framework.SaveAndCheckLogs(t)
10-
require.NoError(t, err)
11-
})
11+
})
1212
```
1313

14-
or customize file assertions
14+
For custom checks, assert logs directly from streams with `StreamCTFContainerLogsFanout`.
1515

1616
```golang
17-
in, err := framework.Load[Cfg](t)
17+
re := regexp.MustCompile(`name=HeadReporter version=\d+`)
18+
t.Cleanup(func() {
19+
err := framework.StreamCTFContainerLogsFanout(
20+
framework.LogStreamConsumer{
21+
Name: "custom-regex-assert",
22+
Consume: func(logStreams map[string]io.ReadCloser) error {
23+
for name, stream := range logStreams {
24+
scanner := bufio.NewScanner(stream)
25+
found := false
26+
for scanner.Scan() {
27+
if re.MatchString(scanner.Text()) {
28+
found = true
29+
break
30+
}
31+
}
32+
if err := scanner.Err(); err != nil {
33+
return fmt.Errorf("scan %s: %w", name, err)
34+
}
35+
if !found {
36+
return fmt.Errorf("missing HeadReporter log in %s", name)
37+
}
38+
}
39+
return nil
40+
},
41+
},
42+
)
1843
require.NoError(t, err)
19-
t.Cleanup(func() {
20-
// save all the logs to default directory "logs/docker-$test_name"
21-
logs, err := framework.SaveContainerLogs(fmt.Sprintf("%s-%s", framework.DefaultCTFLogsDir, t.Name()))
22-
require.NoError(t, err)
23-
// check that CL nodes has no errors (CRIT|PANIC|FATAL) levels
24-
err = framework.CheckCLNodeContainerErrors()
25-
require.NoError(t, err)
26-
// do custom assertions
27-
for _, l := range logs {
28-
matches, err := framework.SearchLogFile(l, " name=HeadReporter version=\\d")
29-
require.NoError(t, err)
30-
_ = matches
31-
}
32-
})
44+
})
3345
```
3446

3547
Full [example](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/framework/examples/myproject/smoke_logs_test.go)

framework/.changeset/v0.15.16.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Fix Sui/Aptos CTF providers tarring the process working directory when `ContractsDir` is empty, eliminating an `archive/tar: write too long` flake in downstream CCIP smoke tests

framework/.changeset/v0.15.17.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Add Docker logs stream fanout and convert existing logs-based functions to consume streams
2+
- Add function for checking & printing traces of panics found in Docker logs

framework/components/blockchain/aptos.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,18 @@ func newAptos(ctx context.Context, in *Input) (*Output, error) {
5555
defaultAptos(in)
5656
containerName := framework.DefaultTCName("blockchain-node")
5757

58-
absPath, err := filepath.Abs(in.ContractsDir)
59-
if err != nil {
60-
return nil, err
58+
var files []testcontainers.ContainerFile
59+
if in.ContractsDir != "" {
60+
absPath, err := filepath.Abs(in.ContractsDir)
61+
if err != nil {
62+
return nil, err
63+
}
64+
files = []testcontainers.ContainerFile{
65+
{
66+
HostFilePath: absPath,
67+
ContainerFilePath: "/",
68+
},
69+
}
6170
}
6271

6372
exposedPorts, bindings, err := framework.GenerateCustomPortsData(in.CustomPorts)
@@ -108,12 +117,7 @@ func newAptos(ctx context.Context, in *Input) (*Output, error) {
108117
},
109118
ImagePlatform: imagePlatform,
110119
Cmd: cmd,
111-
Files: []testcontainers.ContainerFile{
112-
{
113-
HostFilePath: absPath,
114-
ContainerFilePath: "/",
115-
},
116-
},
120+
Files: files,
117121
}
118122

119123
c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{

framework/components/blockchain/sui.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,12 @@ func newSui(ctx context.Context, in *Input) (*Output, error) {
226226
"--force-regenesis",
227227
"--with-faucet",
228228
},
229+
<<<<<<< HEAD
229230
Files: files,
231+
=======
232+
Files: files,
233+
// we need faucet for funding
234+
>>>>>>> main
230235
WaitingFor: wait.ForListeningPort(DefaultFaucetPort).WithStartupTimeout(1 * time.Minute).WithPollInterval(200 * time.Millisecond),
231236
}
232237

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Add ChIP Router client
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package client
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"fmt"
8+
"net"
9+
"net/http"
10+
"strings"
11+
"time"
12+
13+
pkgerrors "github.com/pkg/errors"
14+
)
15+
16+
const (
17+
adminRequestTimeout = 5 * time.Second
18+
)
19+
20+
type RegisterSubscriberRequest struct {
21+
Name string `json:"name"`
22+
Endpoint string `json:"endpoint"`
23+
}
24+
25+
type RegisterSubscriberResponse struct {
26+
ID string `json:"id"`
27+
}
28+
29+
type Client struct {
30+
httpClient *http.Client
31+
adminURL string
32+
grpcURL string
33+
}
34+
35+
func New(ctx context.Context, adminURL, grpcURL string) (*Client, error) {
36+
c := &Client{
37+
httpClient: &http.Client{Timeout: adminRequestTimeout},
38+
adminURL: adminURL,
39+
grpcURL: grpcURL,
40+
}
41+
42+
if !c.isHTTPReady(ctx) {
43+
return nil, fmt.Errorf("chip router admin endpoint is not reachable: %s", c.adminURL)
44+
}
45+
if !c.isTCPReady() {
46+
return nil, fmt.Errorf("chip router grpc endpoint is not reachable: %s", c.grpcURL)
47+
}
48+
return c, nil
49+
}
50+
51+
func (c *Client) RegisterSubscriber(ctx context.Context, name, endpoint string) (string, error) {
52+
body, err := json.Marshal(RegisterSubscriberRequest{Name: name, Endpoint: endpoint})
53+
if err != nil {
54+
return "", pkgerrors.Wrap(err, "marshal chip router register request")
55+
}
56+
57+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, strings.TrimRight(c.adminURL, "/")+"/subscribers", bytes.NewReader(body))
58+
if err != nil {
59+
return "", pkgerrors.Wrap(err, "create chip router register request")
60+
}
61+
req.Header.Set("Content-Type", "application/json")
62+
63+
resp, err := c.httpClient.Do(req)
64+
if err != nil {
65+
return "", pkgerrors.Wrap(err, "perform chip router register request")
66+
}
67+
defer resp.Body.Close()
68+
69+
if resp.StatusCode != http.StatusOK {
70+
return "", fmt.Errorf("chip router register request failed with status %s", resp.Status)
71+
}
72+
73+
var out RegisterSubscriberResponse
74+
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
75+
return "", pkgerrors.Wrap(err, "decode chip router register response")
76+
}
77+
if out.ID == "" {
78+
return "", pkgerrors.New("chip router register response missing subscriber id")
79+
}
80+
81+
return out.ID, nil
82+
}
83+
84+
func (c *Client) UnregisterSubscriber(ctx context.Context, id string) error {
85+
if strings.TrimSpace(id) == "" {
86+
return nil
87+
}
88+
89+
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, strings.TrimRight(c.adminURL, "/")+"/subscribers/"+id, nil)
90+
if err != nil {
91+
return pkgerrors.Wrap(err, "create chip router unregister request")
92+
}
93+
94+
resp, err := c.httpClient.Do(req)
95+
if err != nil {
96+
return pkgerrors.Wrap(err, "perform chip router unregister request")
97+
}
98+
defer resp.Body.Close()
99+
100+
if resp.StatusCode != http.StatusNoContent {
101+
return fmt.Errorf("chip router unregister request failed with status %s", resp.Status)
102+
}
103+
return nil
104+
}
105+
106+
func (c *Client) isHTTPReady(ctx context.Context) bool {
107+
if strings.TrimSpace(c.adminURL) == "" {
108+
return false
109+
}
110+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, strings.TrimRight(c.adminURL, "/")+"/health", nil)
111+
if err != nil {
112+
return false
113+
}
114+
resp, err := c.httpClient.Do(req)
115+
if err != nil {
116+
return false
117+
}
118+
defer resp.Body.Close()
119+
if resp.StatusCode != http.StatusOK {
120+
return false
121+
}
122+
123+
return true
124+
}
125+
126+
func (c *Client) isTCPReady() bool {
127+
dialer := &net.Dialer{Timeout: adminRequestTimeout}
128+
conn, err := dialer.Dial("tcp", c.grpcURL)
129+
if err != nil {
130+
return false
131+
}
132+
_ = conn.Close()
133+
return true
134+
}

framework/components/chiprouter/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/docker/docker v28.5.2+incompatible
88
github.com/docker/go-connections v0.6.0
99
github.com/google/uuid v1.6.0
10+
github.com/pkg/errors v0.9.1
1011
github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20251211140724-319861e514c4
1112
github.com/smartcontractkit/chainlink-testing-framework/framework v0.15.8
1213
github.com/testcontainers/testcontainers-go v0.41.0
@@ -58,7 +59,6 @@ require (
5859
github.com/opencontainers/go-digest v1.0.0 // indirect
5960
github.com/opencontainers/image-spec v1.1.1 // indirect
6061
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
61-
github.com/pkg/errors v0.9.1 // indirect
6262
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
6363
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
6464
github.com/rs/zerolog v1.33.0 // indirect

0 commit comments

Comments
 (0)