diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index a3a4d7dbf..5297db6e9 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -17,6 +17,7 @@ - [Configuration](./framework/configuration.md) - [Test Configuration](./framework/test_configuration_overrides.md) - [Exposing Components](framework/components/state.md) + - [Debugging Tests](framework/components/debug.md) - [Components Cleanup](framework/components/cleanup.md) - [Components Caching](framework/components/caching.md) - [Mocking Services](framework/components/mocking.md) @@ -28,7 +29,6 @@ - [Logs](framework/observability/logs.md) - [Profiling](framework/observability/profiling.md) - [Traces]() - - [Debugger]() - [Blockscout](framework/observability/blockscout.md) - [Components](framework/components/overview.md) - [Blockchains](framework/components/blockchains/overview.md) diff --git a/book/src/framework/components/debug.md b/book/src/framework/components/debug.md new file mode 100644 index 000000000..d0f703b9c --- /dev/null +++ b/book/src/framework/components/debug.md @@ -0,0 +1,44 @@ +# Debugging Tests + +All container logs are saved in a directory named `logs`, which will appear in the same directory where you ran the test after the test is completed. + +For verifying your on-chain components, refer to the [Blockscout documentation](https://docs.blockscout.com/devs/verification/foundry-verification) on smart contract verification. This guide provides detailed instructions on uploading your ABI and verifying your contracts. + +Use `CTF_LOG_LEVEL=trace|debug|info|warn` to debug the framework. + +Use `RESTY_DEBUG=true` to debug any API calls. + +Use `SETH_LOG_LEVEL=trace|debug|info|warn` to debug [Seth](../../libs/seth.md). + +## Using Delve (TBD) + +You can use [Delve]() inside your containers to debug aplications. + +Build them with `go build -gcflags="all=-N -l" -o myapp` and use an example `Dockerfile`: +``` +FROM golang:1.20 + +# Install Delve +RUN go install github.com/go-delve/delve/cmd/dlv@latest + +# Set working directory +WORKDIR /app + +# Copy the application binary and source code (if needed for debugging) +COPY myapp /app/myapp +COPY . /app + +# Expose the port for Delve +EXPOSE 40000 + +# Start Delve in headless mode for remote debugging +ENTRYPOINT ["dlv", "exec", "./myapp", "--headless", "--listen=:40000", "--api-version=2", "--accept-multiclient"] + +``` + +Adding `Delve` to all our components is WIP right now. + +To expose `Delve` port follow this [guide](state.md). + + + diff --git a/book/src/framework/observability/debugger.md b/book/src/framework/observability/debugger.md deleted file mode 100644 index b0f56432b..000000000 --- a/book/src/framework/observability/debugger.md +++ /dev/null @@ -1 +0,0 @@ -# Debugger diff --git a/framework/.changeset/v0.2.11.md b/framework/.changeset/v0.2.11.md new file mode 100644 index 000000000..1d4a41da3 --- /dev/null +++ b/framework/.changeset/v0.2.11.md @@ -0,0 +1,2 @@ +- Save all containers logs by default +- Add more docs about debug \ No newline at end of file diff --git a/framework/config.go b/framework/config.go index c53aa28d9..e9b418393 100644 --- a/framework/config.go +++ b/framework/config.go @@ -130,6 +130,8 @@ func Load[X any](t *testing.T) (*X, error) { t.Cleanup(func() { err := Store[X](input) require.NoError(t, err) + err = WriteAllContainersLogs() + require.NoError(t, err) }) // TODO: not all the people have AWS access, sadly enough, uncomment when granted //if os.Getenv(EnvVarAWSSecretsManager) == "true" { diff --git a/framework/docker.go b/framework/docker.go index 588fef653..281c798da 100644 --- a/framework/docker.go +++ b/framework/docker.go @@ -4,6 +4,7 @@ import ( "archive/tar" "bytes" "context" + "encoding/binary" "errors" "fmt" "github.com/docker/docker/api/types/container" @@ -14,10 +15,15 @@ import ( "io" "os" "os/exec" + "path/filepath" "strings" "sync" ) +const ( + DefaultCTFLogsDir = "logs" +) + func IsDockerRunning() bool { cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { @@ -223,3 +229,67 @@ func (dc *DockerClient) copyToContainer(containerID, sourceFile, targetPath stri } return nil } + +// WriteAllContainersLogs writes all Docker container logs to the default logs directory +func WriteAllContainersLogs() error { + L.Info().Msg("Writing Docker containers logs") + if _, err := os.Stat(DefaultCTFLogsDir); os.IsNotExist(err) { + if err := os.MkdirAll(DefaultCTFLogsDir, 0755); err != nil { + return fmt.Errorf("failed to create directory %s: %w", DefaultCTFLogsDir, err) + } + } + provider, err := tc.NewDockerProvider() + if err != nil { + return fmt.Errorf("failed to create Docker provider: %w", err) + } + containers, err := provider.Client().ContainerList(context.Background(), container.ListOptions{All: true}) + if err != nil { + return fmt.Errorf("failed to list Docker containers: %w", err) + } + + for _, containerInfo := range containers { + containerName := containerInfo.Names[0] + logOptions := container.LogsOptions{ShowStdout: true, ShowStderr: true} + logs, err := provider.Client().ContainerLogs(context.Background(), containerInfo.ID, logOptions) + if err != nil { + L.Error().Err(err).Str("Container", containerName).Msg("failed to fetch logs for container") + continue + } + logFilePath := filepath.Join(DefaultCTFLogsDir, fmt.Sprintf("%s.log", containerName)) + logFile, err := os.Create(logFilePath) + if err != nil { + L.Error().Err(err).Str("Container", containerName).Msg("failed to create container log file") + continue + } + // Parse and write logs + header := make([]byte, 8) // Docker stream header is 8 bytes + for { + _, err := io.ReadFull(logs, header) + if err == io.EOF { + break + } + if err != nil { + L.Error().Err(err).Str("Container", containerName).Msg("failed to read log stream header") + break + } + + // Extract log message size + msgSize := binary.BigEndian.Uint32(header[4:8]) + + // Read the log message + msg := make([]byte, msgSize) + _, err = io.ReadFull(logs, msg) + if err != nil { + L.Error().Err(err).Str("Container", containerName).Msg("failed to read log message") + break + } + + // Write the log message to the file + if _, err := logFile.Write(msg); err != nil { + L.Error().Err(err).Str("Container", containerName).Msg("failed to write log message to file") + break + } + } + } + return nil +} diff --git a/wasp/examples/go.sum b/wasp/examples/go.sum index c5c070450..f4f6fb063 100644 --- a/wasp/examples/go.sum +++ b/wasp/examples/go.sum @@ -73,8 +73,8 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0 github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.6 h1:U68crOE3y3MPttCMQGywZOLrTeF5HHJ3/vDBCJn9/bA= github.com/OneOfOne/xxhash v1.2.6/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= @@ -188,8 +188,7 @@ github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -814,8 +813,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=