mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-01-10 06:28:04 -05:00
225 lines
6.6 KiB
Go
225 lines
6.6 KiB
Go
package testcontainers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/scroll-tech/go-ethereum/ethclient"
|
|
"github.com/testcontainers/testcontainers-go"
|
|
"github.com/testcontainers/testcontainers-go/modules/compose"
|
|
"github.com/testcontainers/testcontainers-go/modules/postgres"
|
|
"github.com/testcontainers/testcontainers-go/wait"
|
|
"gorm.io/gorm"
|
|
|
|
"scroll-tech/common/database"
|
|
)
|
|
|
|
// TestcontainerApps testcontainers struct
|
|
type TestcontainerApps struct {
|
|
postgresContainer *postgres.PostgresContainer
|
|
l2GethContainer *testcontainers.DockerContainer
|
|
poSL1Container compose.ComposeStack
|
|
|
|
// common time stamp in nanoseconds.
|
|
Timestamp int
|
|
}
|
|
|
|
// NewTestcontainerApps returns new instance of TestcontainerApps struct
|
|
func NewTestcontainerApps() *TestcontainerApps {
|
|
timestamp := time.Now().Nanosecond()
|
|
return &TestcontainerApps{
|
|
Timestamp: timestamp,
|
|
}
|
|
}
|
|
|
|
// StartPostgresContainer starts a postgres container
|
|
func (t *TestcontainerApps) StartPostgresContainer() error {
|
|
if t.postgresContainer != nil && t.postgresContainer.IsRunning() {
|
|
return nil
|
|
}
|
|
postgresContainer, err := postgres.RunContainer(context.Background(),
|
|
testcontainers.WithImage("postgres"),
|
|
postgres.WithDatabase("test_db"),
|
|
postgres.WithPassword("123456"),
|
|
testcontainers.WithWaitStrategy(
|
|
wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)),
|
|
)
|
|
if err != nil {
|
|
log.Printf("failed to start postgres container: %s", err)
|
|
return err
|
|
}
|
|
t.postgresContainer = postgresContainer
|
|
return nil
|
|
}
|
|
|
|
// StartL2GethContainer starts a L2Geth container
|
|
func (t *TestcontainerApps) StartL2GethContainer() error {
|
|
if t.l2GethContainer != nil && t.l2GethContainer.IsRunning() {
|
|
return nil
|
|
}
|
|
req := testcontainers.ContainerRequest{
|
|
Image: "scroll_l2geth",
|
|
ExposedPorts: []string{"8546/tcp", "8545/tcp"},
|
|
WaitingFor: wait.ForAll(
|
|
wait.ForListeningPort("8546").WithStartupTimeout(100*time.Second),
|
|
wait.ForListeningPort("8545").WithStartupTimeout(100*time.Second),
|
|
),
|
|
}
|
|
genericContainerReq := testcontainers.GenericContainerRequest{
|
|
ContainerRequest: req,
|
|
Started: true,
|
|
}
|
|
container, err := testcontainers.GenericContainer(context.Background(), genericContainerReq)
|
|
if err != nil {
|
|
log.Printf("failed to start scroll_l2geth container: %s", err)
|
|
return err
|
|
}
|
|
t.l2GethContainer, _ = container.(*testcontainers.DockerContainer)
|
|
return nil
|
|
}
|
|
|
|
// StartPoSL1Container starts the PoS L1 container by running the associated Docker Compose configuration
|
|
func (t *TestcontainerApps) StartPoSL1Container() error {
|
|
var (
|
|
err error
|
|
rootDir string
|
|
dockerComposeFile string
|
|
)
|
|
|
|
if rootDir, err = findProjectRootDir(); err != nil {
|
|
return fmt.Errorf("failed to find project root directory: %v", err)
|
|
}
|
|
|
|
dockerComposeFile = filepath.Join(rootDir, "common", "testcontainers", "docker-compose.yml")
|
|
|
|
if t.poSL1Container, err = compose.NewDockerCompose([]string{dockerComposeFile}...); err != nil {
|
|
return err
|
|
}
|
|
err = t.poSL1Container.WaitForService("geth", wait.NewHTTPStrategy("/").
|
|
WithPort("8545/tcp").
|
|
WithStartupTimeout(15*time.Second)).
|
|
Up(context.Background())
|
|
if err != nil {
|
|
t.poSL1Container = nil
|
|
return fmt.Errorf("failed to start PoS L1 container: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetPoSL1EndPoint returns the endpoint of the running PoS L1 endpoint
|
|
func (t *TestcontainerApps) GetPoSL1EndPoint() (string, error) {
|
|
if t.poSL1Container == nil {
|
|
return "", fmt.Errorf("PoS L1 container is not running")
|
|
}
|
|
contrainer, err := t.poSL1Container.ServiceContainer(context.Background(), "geth")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return contrainer.PortEndpoint(context.Background(), "8545/tcp", "http")
|
|
}
|
|
|
|
// GetPoSL1Client returns a ethclient by dialing running PoS L1 client
|
|
func (t *TestcontainerApps) GetPoSL1Client() (*ethclient.Client, error) {
|
|
endpoint, err := t.GetPoSL1EndPoint()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ethclient.Dial(endpoint)
|
|
}
|
|
|
|
// GetDBEndPoint returns the endpoint of the running postgres container
|
|
func (t *TestcontainerApps) GetDBEndPoint() (string, error) {
|
|
if t.postgresContainer == nil || !t.postgresContainer.IsRunning() {
|
|
return "", fmt.Errorf("postgres is not running")
|
|
}
|
|
return t.postgresContainer.ConnectionString(context.Background(), "sslmode=disable")
|
|
}
|
|
|
|
// GetL2GethEndPoint returns the endpoint of the running L2Geth container
|
|
func (t *TestcontainerApps) GetL2GethEndPoint() (string, error) {
|
|
if t.l2GethContainer == nil || !t.l2GethContainer.IsRunning() {
|
|
return "", fmt.Errorf("l2 geth is not running")
|
|
}
|
|
endpoint, err := t.l2GethContainer.PortEndpoint(context.Background(), "8546/tcp", "ws")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return endpoint, nil
|
|
}
|
|
|
|
// GetGormDBClient returns a gorm.DB by connecting to the running postgres container
|
|
func (t *TestcontainerApps) GetGormDBClient() (*gorm.DB, error) {
|
|
endpoint, err := t.GetDBEndPoint()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dbCfg := &database.Config{
|
|
DSN: endpoint,
|
|
DriverName: "postgres",
|
|
MaxOpenNum: 200,
|
|
MaxIdleNum: 20,
|
|
}
|
|
return database.InitDB(dbCfg)
|
|
}
|
|
|
|
// GetL2GethClient returns a ethclient by dialing running L2Geth
|
|
func (t *TestcontainerApps) GetL2GethClient() (*ethclient.Client, error) {
|
|
endpoint, err := t.GetL2GethEndPoint()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
client, err := ethclient.Dial(endpoint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return client, nil
|
|
}
|
|
|
|
// Free stops all running containers
|
|
func (t *TestcontainerApps) Free() {
|
|
ctx := context.Background()
|
|
if t.postgresContainer != nil && t.postgresContainer.IsRunning() {
|
|
if err := t.postgresContainer.Terminate(ctx); err != nil {
|
|
log.Printf("failed to stop postgres container: %s", err)
|
|
}
|
|
}
|
|
if t.l2GethContainer != nil && t.l2GethContainer.IsRunning() {
|
|
if err := t.l2GethContainer.Terminate(ctx); err != nil {
|
|
log.Printf("failed to stop scroll_l2geth container: %s", err)
|
|
}
|
|
}
|
|
if t.poSL1Container != nil {
|
|
if err := t.poSL1Container.Down(context.Background(), compose.RemoveOrphans(true), compose.RemoveVolumes(true), compose.RemoveImagesLocal); err != nil {
|
|
log.Printf("failed to stop PoS L1 container: %s", err)
|
|
} else {
|
|
t.poSL1Container = nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// findProjectRootDir find project root directory
|
|
func findProjectRootDir() (string, error) {
|
|
currentDir, err := os.Getwd()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get working directory: %w", err)
|
|
}
|
|
|
|
for {
|
|
_, err := os.Stat(filepath.Join(currentDir, "go.work"))
|
|
if err == nil {
|
|
return currentDir, nil
|
|
}
|
|
|
|
parentDir := filepath.Dir(currentDir)
|
|
if parentDir == currentDir {
|
|
return "", fmt.Errorf("go.work file not found in any parent directory")
|
|
}
|
|
|
|
currentDir = parentDir
|
|
}
|
|
}
|