Skip to content

Commit 9311f82

Browse files
committed
blockscout urls and docs, run logs download in parallel and ignore local stuff
1 parent 4457e19 commit 9311f82

File tree

15 files changed

+206
-63
lines changed

15 files changed

+206
-63
lines changed

book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- [Mocking Services](framework/components/mocking.md)
2424
- [Copying Files](framework/copying_files.md)
2525
- [External Environment](framework/components/external.md)
26+
- [Troubleshooting](framework/components/troubleshooting.md)
2627
- [Secrets]()
2728
- [Observability Stack](framework/observability/observability_stack.md)
2829
- [Metrics](framework/observability/metrics.md)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Troubleshooting
2+
3+
## Can't run `anvil`, issue with `Rosetta`
4+
```
5+
2024/11/27 15:20:27 ⏳ Waiting for container id 79f8a68c07cc image: f4hrenh9it/foundry:latest. Waiting for: &{Port:8546 timeout:0x14000901278 PollInterval:100ms skipInternalCheck:false}
6+
2024/11/27 15:20:27 container logs (all exposed ports, [8546/tcp], were not mapped in 5s: port 8546/tcp is not mapped yet
7+
wait until ready: check target: retries: 1, port: "", last err: container exited with code 133):
8+
rosetta error: Rosetta is only intended to run on Apple Silicon with a macOS host using Virtualization.framework with Rosetta mode enabled
9+
```
10+
#### Solution
11+
12+
Update your docker to `Docker version 27.3.1, build ce12230`

book/src/framework/getting_started.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
- `Docker` [OrbStack](https://orbstack.dev/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/), we recommend OrbStack (faster, smaller memory footprint)
55
- [Golang](https://go.dev/doc/install)
66

7+
Tested with
8+
```
9+
Docker version 27.3.1
10+
OrbStack Version: 1.8.2 (1080200)
11+
```
12+
713
## Test setup
814

915
To start writing tests create a directory for your project with `go.mod` and add a package

framework/cmd/blockscout.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ package main
33
import (
44
"fmt"
55
"github.com/smartcontractkit/chainlink-testing-framework/framework"
6+
"os"
67
"path/filepath"
78
)
89

9-
func blockscoutUp() error {
10+
func blockscoutUp(url string) error {
1011
framework.L.Info().Msg("Creating local Blockscout stack")
1112
if err := extractAllFiles("observability"); err != nil {
1213
return err
1314
}
15+
os.Setenv("BLOCKSCOUT_RPC_URL", url)
1416
err := runCommand("bash", "-c", fmt.Sprintf(`
1517
cd %s && \
1618
docker compose up -d
@@ -23,8 +25,9 @@ func blockscoutUp() error {
2325
return nil
2426
}
2527

26-
func blockscoutDown() error {
28+
func blockscoutDown(url string) error {
2729
framework.L.Info().Msg("Removing local Blockscout stack")
30+
os.Setenv("BLOCKSCOUT_RPC_URL", url)
2831
err := runCommand("bash", "-c", fmt.Sprintf(`
2932
cd %s && \
3033
docker compose down -v

framework/cmd/interactive.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ import (
1616
)
1717

1818
type nodeSetForm struct {
19-
Network string
20-
CLVersion string
21-
Nodes int
22-
Observability bool
23-
Blockscout bool
19+
Network string
20+
CLVersion string
21+
Nodes int
22+
Observability bool
23+
Blockscout bool
24+
BlockscoutRPCURL string
2425
}
2526

2627
func createComponentsFromForm(form *nodeSetForm) error {
@@ -83,7 +84,7 @@ func createComponentsFromForm(form *nodeSetForm) error {
8384
}
8485
switch form.Blockscout {
8586
case true:
86-
if err := blockscoutUp(); err != nil {
87+
if err := blockscoutUp(form.BlockscoutRPCURL); err != nil {
8788
return err
8889
}
8990
}
@@ -107,7 +108,7 @@ func cleanup(form *nodeSetForm) error {
107108
}
108109
switch form.Blockscout {
109110
case true:
110-
if err := blockscoutDown(); err != nil {
111+
if err := blockscoutDown(form.BlockscoutRPCURL); err != nil {
111112
return err
112113
}
113114
}
@@ -156,6 +157,13 @@ Docker Desktop (https://www.docker.com/products/docker-desktop/)
156157
huh.NewConfirm().
157158
Title("Do you need to spin up a Blockscout stack?").
158159
Value(&f.Blockscout),
160+
huh.NewSelect[string]().
161+
Title("To which blockchain node you want Blockscout to connect?").
162+
Options(
163+
huh.NewOption("Network 1", "http://host.docker.internal:8545"),
164+
huh.NewOption("Network 2", "http://host.docker.internal:8550"),
165+
).
166+
Value(&f.BlockscoutRPCURL),
159167
),
160168
)
161169

framework/cmd/main.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,31 +114,44 @@ func main() {
114114
Name: "blockscout",
115115
Aliases: []string{"bs"},
116116
Usage: "Controls local Blockscout stack",
117+
Flags: []cli.Flag{
118+
&cli.StringFlag{
119+
Name: "rpc",
120+
Aliases: []string{"r"},
121+
Usage: "RPC URL for blockchain node to index",
122+
Value: "http://host.docker.internal:8545",
123+
},
124+
},
117125
Subcommands: []*cli.Command{
118126
{
119127
Name: "up",
120128
Usage: "ctf bs up",
121129
Aliases: []string{"u"},
122130
Description: "Spins up Blockscout stack",
123-
Action: func(c *cli.Context) error { return blockscoutUp() },
131+
Action: func(c *cli.Context) error {
132+
return blockscoutUp(c.String("rpc"))
133+
},
124134
},
125135
{
126136
Name: "down",
127137
Usage: "ctf bs down",
128138
Aliases: []string{"d"},
129139
Description: "Removes Blockscout stack, wipes all Blockscout databases data",
130-
Action: func(c *cli.Context) error { return blockscoutDown() },
140+
Action: func(c *cli.Context) error {
141+
return blockscoutDown(c.String("rpc"))
142+
},
131143
},
132144
{
133145
Name: "reboot",
134146
Usage: "ctf bs reboot or ctf bs r",
135147
Aliases: []string{"r"},
136148
Description: "Reboots Blockscout stack",
137149
Action: func(c *cli.Context) error {
138-
if err := blockscoutDown(); err != nil {
150+
rpc := c.String("rpc")
151+
if err := blockscoutDown(rpc); err != nil {
139152
return err
140153
}
141-
return blockscoutUp()
154+
return blockscoutUp(rpc)
142155
},
143156
},
144157
},

framework/cmd/observability/blockscout/docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ services:
2828
- db:database
2929
environment:
3030
ETHEREUM_JSONRPC_VARIANT: 'geth'
31+
ETHEREUM_JSONRPC_HTTP_URL: ${BLOCKSCOUT_RPC_URL}
32+
ETHEREUM_JSONRPC_TRACE_URL: ${BLOCKSCOUT_RPC_URL}
3133

3234
visualizer:
3335
extends:

framework/components/simple_node_set/node_set.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,12 @@ func printURLs(out *Output) {
6262
if out == nil {
6363
return
6464
}
65-
httpURLs, p2pURLs, pgURLs := make([]string, 0), make([]string, 0), make([]string, 0)
65+
httpURLs, _, pgURLs := make([]string, 0), make([]string, 0), make([]string, 0)
6666
for _, n := range out.CLNodes {
6767
httpURLs = append(httpURLs, n.Node.HostURL)
6868
pgURLs = append(pgURLs, n.PostgreSQL.Url)
6969
}
7070
framework.L.Info().Any("UI", httpURLs).Send()
71-
framework.L.Debug().Any("P2P", p2pURLs).Send()
7271
framework.L.Debug().Any("DB", pgURLs).Send()
7372
}
7473

framework/docker.go

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/docker/go-connections/nat"
1313
"github.com/google/uuid"
1414
tc "github.com/testcontainers/testcontainers-go"
15+
"golang.org/x/sync/errgroup"
1516
"io"
1617
"os"
1718
"os/exec"
@@ -21,6 +22,7 @@ import (
2122
)
2223

2324
const (
25+
EnvVarSaveLogs = "CTF_SAVE_LOGS"
2426
DefaultCTFLogsDir = "logs"
2527
)
2628

@@ -230,6 +232,23 @@ func (dc *DockerClient) copyToContainer(containerID, sourceFile, targetPath stri
230232
return nil
231233
}
232234

235+
func in(s string, substrings []string) bool {
236+
for _, substr := range substrings {
237+
if strings.Contains(s, substr) {
238+
return true
239+
}
240+
}
241+
return false
242+
}
243+
244+
func isLocalToolDockerContainer(containerName string) bool {
245+
if in(containerName, []string{"/sig-provider", "/stats", "/stats-db", "/db", "/backend", "/promtail", "/compose", "/blockscout", "/frontend", "/user-ops-indexer", "/visualizer", "/redis-db", "/proxy"}) {
246+
L.Debug().Str("Container", containerName).Msg("Ignoring local tool container output")
247+
return true
248+
}
249+
return false
250+
}
251+
233252
// WriteAllContainersLogs writes all Docker container logs to the default logs directory
234253
func WriteAllContainersLogs() error {
235254
L.Info().Msg("Writing Docker containers logs")
@@ -247,49 +266,58 @@ func WriteAllContainersLogs() error {
247266
return fmt.Errorf("failed to list Docker containers: %w", err)
248267
}
249268

269+
eg := &errgroup.Group{}
270+
250271
for _, containerInfo := range containers {
251-
containerName := containerInfo.Names[0]
252-
logOptions := container.LogsOptions{ShowStdout: true, ShowStderr: true}
253-
logs, err := provider.Client().ContainerLogs(context.Background(), containerInfo.ID, logOptions)
254-
if err != nil {
255-
L.Error().Err(err).Str("Container", containerName).Msg("failed to fetch logs for container")
256-
continue
257-
}
258-
logFilePath := filepath.Join(DefaultCTFLogsDir, fmt.Sprintf("%s.log", containerName))
259-
logFile, err := os.Create(logFilePath)
260-
if err != nil {
261-
L.Error().Err(err).Str("Container", containerName).Msg("failed to create container log file")
262-
continue
263-
}
264-
// Parse and write logs
265-
header := make([]byte, 8) // Docker stream header is 8 bytes
266-
for {
267-
_, err := io.ReadFull(logs, header)
268-
if err == io.EOF {
269-
break
272+
eg.Go(func() error {
273+
containerName := containerInfo.Names[0]
274+
if isLocalToolDockerContainer(containerName) {
275+
return nil
270276
}
277+
L.Debug().Str("Container", containerName).Msg("Collecting logs")
278+
logOptions := container.LogsOptions{ShowStdout: true, ShowStderr: true}
279+
logs, err := provider.Client().ContainerLogs(context.Background(), containerInfo.ID, logOptions)
271280
if err != nil {
272-
L.Error().Err(err).Str("Container", containerName).Msg("failed to read log stream header")
273-
break
281+
L.Error().Err(err).Str("Container", containerName).Msg("failed to fetch logs for container")
282+
return err
274283
}
275-
276-
// Extract log message size
277-
msgSize := binary.BigEndian.Uint32(header[4:8])
278-
279-
// Read the log message
280-
msg := make([]byte, msgSize)
281-
_, err = io.ReadFull(logs, msg)
284+
logFilePath := filepath.Join(DefaultCTFLogsDir, fmt.Sprintf("%s.log", containerName))
285+
logFile, err := os.Create(logFilePath)
282286
if err != nil {
283-
L.Error().Err(err).Str("Container", containerName).Msg("failed to read log message")
284-
break
287+
L.Error().Err(err).Str("Container", containerName).Msg("failed to create container log file")
288+
return err
285289
}
290+
// Parse and write logs
291+
header := make([]byte, 8) // Docker stream header is 8 bytes
292+
for {
293+
_, err := io.ReadFull(logs, header)
294+
if err == io.EOF {
295+
break
296+
}
297+
if err != nil {
298+
L.Error().Err(err).Str("Container", containerName).Msg("failed to read log stream header")
299+
break
300+
}
286301

287-
// Write the log message to the file
288-
if _, err := logFile.Write(msg); err != nil {
289-
L.Error().Err(err).Str("Container", containerName).Msg("failed to write log message to file")
290-
break
302+
// Extract log message size
303+
msgSize := binary.BigEndian.Uint32(header[4:8])
304+
305+
// Read the log message
306+
msg := make([]byte, msgSize)
307+
_, err = io.ReadFull(logs, msg)
308+
if err != nil {
309+
L.Error().Err(err).Str("Container", containerName).Msg("failed to read log message")
310+
break
311+
}
312+
313+
// Write the log message to the file
314+
if _, err := logFile.Write(msg); err != nil {
315+
L.Error().Err(err).Str("Container", containerName).Msg("failed to write log message to file")
316+
break
317+
}
291318
}
292-
}
319+
return nil
320+
})
293321
}
294-
return nil
322+
return eg.Wait()
295323
}

framework/examples/myproject/fork.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33

44
[blockchain_dst]
55
chain_id = "2337"
6-
# docker_cmd_params = ["--fork-url", "wss://avalanche-fuji-c-chain-rpc.publicnode.com", "--auto-impersonate"]
6+
# docker_cmd_params = ["--fork-url", "wss://avalanche-fuji-c-chain-rpc.publicnode.com", "--auto-impersonate", "-b", "1"]
7+
docker_cmd_params = ["-b", "1"]
78
image = "f4hrenh9it/foundry:latest"
89
port = "8545"
910
type = "anvil"
10-
docker_cmd_params = ["-b", "1"]
1111

1212
[blockchain_src]
1313
chain_id = "3337"
14-
# docker_cmd_params = ["--fork-url", "wss://avalanche-fuji-c-chain-rpc.publicnode.com", "--auto-impersonate"]
14+
# docker_cmd_params = ["--fork-url", "wss://avalanche-fuji-c-chain-rpc.publicnode.com", "--auto-impersonate", "-b", "1"]
15+
docker_cmd_params = ["-b", "1"]
1516
image = "f4hrenh9it/foundry:latest"
1617
port = "8555"
1718
type = "anvil"
18-
docker_cmd_params = ["-b", "1"]

0 commit comments

Comments
 (0)