Skip to content

Commit 82a7b68

Browse files
ferranbtcanercidam
andauthored
Integration tests for Rbuilder (#335)
This PR adds a new component for the Rbuilder: - Adds the Rbuilder component. - Create a new type of data volumes which **does not** use named Docker volumes but local mount points in temp dirs. This is because with named Docker volumes, the Reth mdx database fails to be shared with the Rbuilder component. - Add a test recipe that test whether the Rbuilder component can work with the Reth component deployed on the L1 recipe which can be enabled with the `--rbuilder` flag. --------- Co-authored-by: Caner Çıdam <canercidam01@gmail.com>
1 parent eda0e8e commit 82a7b68

17 files changed

+387
-35
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ test: ## Run tests
3030
.PHONY: integration-test
3131
integration-test: ## Run integration tests
3232
INTEGRATION_TESTS=true go test -v -count=1 ./playground/... -run TestRecipe
33+
INTEGRATION_TESTS=true go test -v -count=1 ./playground/... -run TestComponent
3334

3435
.PHONY: generate-docs
3536
generate-docs: ## Auto-generate recipe docs

docs/recipes/buildernet.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Deploy a full L1 stack with mev-boost and builder-hub.
88
- `builder-config` (string): Builder config in YAML format. Default to ''.
99
- `builder-ip` (string): IP address of the external builder to register in BuilderHub. Default to '127.0.0.1'.
1010
- `latest-fork` (bool): use the latest fork. Default to 'false'.
11+
- `rbuilder` (bool): include rbuilder in the recipe. Default to 'false'.
1112
- `secondary-el` (string): Address or port to use for the secondary EL (execution layer); Can be a port number (e.g., '8551') in which case the full URL is derived as `http://localhost:<port>` or a complete URL (e.g., `http://docker-container-name:8551`), use `http://host.docker.internal:<port>` to reach a secondary execution client that runs on your host and not within Docker.. Default to ''.
1213
- `use-native-reth` (bool): use the native reth binary. Default to 'false'.
1314
- `use-reth-for-validation` (bool): use reth for validation. Default to 'false'.

docs/recipes/l1.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Deploy a full L1 stack with mev-boost.
66

77
- `block-time` (duration): Block time to use for the L1. Default to '12s'.
88
- `latest-fork` (bool): use the latest fork. Default to 'false'.
9+
- `rbuilder` (bool): include rbuilder in the recipe. Default to 'false'.
910
- `secondary-el` (string): Address or port to use for the secondary EL (execution layer); Can be a port number (e.g., '8551') in which case the full URL is derived as `http://localhost:<port>` or a complete URL (e.g., `http://docker-container-name:8551`), use `http://host.docker.internal:<port>` to reach a secondary execution client that runs on your host and not within Docker.. Default to ''.
1011
- `use-native-reth` (bool): use the native reth binary. Default to 'false'.
1112
- `use-reth-for-validation` (bool): use reth for validation. Default to 'false'.

main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,11 @@ func runIt(recipe playground.Recipe) error {
588588
slog.Info("Output folder: " + out.Dst())
589589
slog.Info("")
590590

591+
if utils.GetSessionTempDirCount() > 20 {
592+
slog.Warn("Too many temp dirs - please later remove " + utils.TempPlaygroundDirPath() + " (auto-removed at reboot)")
593+
slog.Info("")
594+
}
595+
591596
// parse the overrides
592597
overrides := map[string]string{}
593598
for _, val := range withOverrides {

playground/components.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"strings"
88
"time"
99

10+
_ "embed"
11+
1012
"github.com/ethereum/go-ethereum/common/hexutil"
1113
mevboostrelay "github.com/flashbots/builder-playground/mev-boost-relay"
1214
"github.com/flashbots/go-boost-utils/bls"
@@ -503,7 +505,7 @@ func (r *RethEL) Apply(ctx *ExContext) *Component {
503505
WithRelease(rethELRelease).
504506
WithArtifact("/data/genesis.json", "genesis.json").
505507
WithArtifact("/data/jwtsecret", "jwtsecret").
506-
WithVolume("data", "/data_reth")
508+
WithVolume("data", "/data_reth", true)
507509

508510
UseHealthmon(component, svc, healthmonExecution)
509511

@@ -746,6 +748,31 @@ func (m *MevBoost) Apply(ctx *ExContext) *Component {
746748
return component
747749
}
748750

751+
//go:embed utils/rbuilder-config.toml.tmpl
752+
var rbuilderConfigToml string
753+
754+
type Rbuilder struct{}
755+
756+
func (r *Rbuilder) Apply(ctx *ExContext) *Component {
757+
component := NewComponent("rbuilder")
758+
759+
// TODO: Handle error
760+
ctx.Output.WriteFile("rbuilder-config.toml", rbuilderConfigToml)
761+
762+
component.NewService("rbuilder").
763+
WithImage("ghcr.io/flashbots/rbuilder").
764+
WithTag("sha-7efdc0b").
765+
WithArtifact("/data/rbuilder-config.toml", "rbuilder-config.toml").
766+
WithArtifact("/data/genesis.json", "genesis.json").
767+
WithVolume("shared:el-data", "/data_reth", true).
768+
DependsOnHealthy("el").
769+
WithArgs(
770+
"run", "/data/rbuilder-config.toml",
771+
)
772+
773+
return component
774+
}
775+
749776
type nullService struct{}
750777

751778
func (n *nullService) Apply(ctx *ExContext) *Component {

playground/components_test.go

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"os"
1212
"path/filepath"
1313
"regexp"
14+
"runtime"
1415
"strings"
1516
"testing"
1617
"time"
@@ -94,6 +95,44 @@ func TestRecipeL1UseNativeReth(t *testing.T) {
9495
})
9596
}
9697

98+
type rbuilderRecipe struct {
99+
*mockRecipe
100+
l1 *L1Recipe
101+
}
102+
103+
func (r *rbuilderRecipe) Artifacts() *ArtifactsBuilder {
104+
return r.l1.Artifacts()
105+
}
106+
107+
func (r *rbuilderRecipe) Apply(ctx *ExContext) *Component {
108+
c := NewComponent("rbuilder-test-recipe")
109+
110+
c.AddService(ctx, r.l1)
111+
c.AddComponent(ctx, &Rbuilder{})
112+
113+
return c
114+
}
115+
116+
func TestComponentRbuilder(t *testing.T) {
117+
// TODO: Re-enable this for all architectures when the rbuilder container flow works.
118+
if runtime.GOARCH != "arm64" {
119+
t.Skip("Skipping rbuilder component test on non-arm64 architecture for now")
120+
}
121+
tt := newTestFramework(t)
122+
defer tt.Close()
123+
124+
recipe := &rbuilderRecipe{
125+
l1: &L1Recipe{
126+
// TODO: We might have to change things from rbuilder-config
127+
// if the time is lower than 12 seconds.
128+
blockTime: 12 * time.Second,
129+
},
130+
}
131+
132+
tt.test(recipe, nil)
133+
tt.WaitForBlock("el", 1)
134+
}
135+
97136
func TestRecipeBuilderHub(t *testing.T) {
98137
tt := newTestFramework(t)
99138
defer tt.Close()
@@ -200,9 +239,12 @@ func (tt *testFramework) test(component ComponentGen, args []string) *Manifest {
200239
t.Fatal(err)
201240
}
202241

242+
homeDir, err := GetHomeDir()
243+
require.NoError(t, err)
244+
203245
o := &output{
204246
dst: e2eTestDir,
205-
homeDir: filepath.Join(e2eTestDir, "artifacts"),
247+
homeDir: homeDir,
206248
}
207249

208250
exCtx := &ExContext{
@@ -264,6 +306,28 @@ func (tt *testFramework) Close() {
264306
}
265307
}
266308

309+
// WaitForBlock waits for the specified service to reach the given block number.
310+
// It polls the service's HTTP endpoint and fails the test if the block isn't
311+
// reached within 1 minute or if the runner exits with an error.
312+
func (tt *testFramework) WaitForBlock(service string, num uint64) {
313+
elService := tt.runner.manifest.MustGetService(service)
314+
rethURL := fmt.Sprintf("http://localhost:%d", elService.MustGetPort("http").HostPort)
315+
316+
waitForBlockCh := make(chan error)
317+
go func() {
318+
waitForBlockCh <- waitForBlock(rethURL, num, 1*time.Minute)
319+
}()
320+
321+
select {
322+
case err := <-waitForBlockCh:
323+
if err != nil {
324+
tt.t.Fatal(err)
325+
}
326+
case err := <-tt.runner.ExitErr():
327+
tt.t.Fatal(err)
328+
}
329+
}
330+
267331
func toSnakeCase(s string) string {
268332
// Insert underscore before uppercase letters
269333
re := regexp.MustCompile("([a-z0-9])([A-Z])")

playground/custom_recipes_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ func (m *mockRecipe) Description() string {
399399
}
400400

401401
func (m *mockRecipe) Flags() *flag.FlagSet {
402-
return nil
402+
return flag.NewFlagSet("", 0)
403403
}
404404

405405
func (m *mockRecipe) Artifacts() *ArtifactsBuilder {

playground/local_runner.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -536,10 +536,16 @@ func (d *LocalRunner) toDockerComposeService(s *Service) (map[string]interface{}
536536

537537
// create the bind volumes
538538
var createdVolumes []string
539-
for localPath, volumeName := range s.VolumesMapped {
540-
dockerVolumeName := d.createVolumeName(s.Name, volumeName)
541-
volumes[dockerVolumeName] = localPath
542-
createdVolumes = append(createdVolumes, dockerVolumeName)
539+
for localPath, volume := range s.VolumesMapped {
540+
dockerVolumeName := d.createVolumeName(s.Name, volume.Name)
541+
542+
if volume.IsLocal {
543+
absPath := utils.MustGetVolumeDir(d.manifest.ID, dockerVolumeName)
544+
volumes[absPath] = localPath
545+
} else {
546+
volumes[dockerVolumeName] = localPath
547+
createdVolumes = append(createdVolumes, dockerVolumeName)
548+
}
543549
}
544550

545551
volumesInLine := []string{}
@@ -803,8 +809,8 @@ func (d *LocalRunner) runOnHost(ss *Service) error {
803809

804810
// Create the volumes for this service
805811
volumesMapped := map[string]string{}
806-
for pathInDocker, volumeName := range ss.VolumesMapped {
807-
volumeDirAbsPath, err := d.createVolumeDir(ss.Name, volumeName)
812+
for pathInDocker, volume := range ss.VolumesMapped {
813+
volumeDirAbsPath, err := d.createVolumeDir(ss.Name, volume.Name)
808814
if err != nil {
809815
return err
810816
}

playground/manifest.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ func (p *Component) AddComponent(ctx *ExContext, gen ComponentGen) {
6666
p.Inner = append(p.Inner, gen.Apply(ctx))
6767
}
6868

69-
func (p *Component) AddService(srv ComponentGen) {
70-
p.Inner = append(p.Inner, srv.Apply(nil))
69+
func (p *Component) AddService(ctx *ExContext, srv ComponentGen) {
70+
p.Inner = append(p.Inner, srv.Apply(ctx))
7171
}
7272

7373
func componentToManifest(p *Component) []*Service {
@@ -359,8 +359,8 @@ type Service struct {
359359
Ports []*Port `json:"ports,omitempty"`
360360
NodeRefs []*NodeRef `json:"node_refs,omitempty"`
361361

362-
FilesMapped map[string]string `json:"files_mapped,omitempty"`
363-
VolumesMapped map[string]string `json:"volumes_mapped,omitempty"`
362+
FilesMapped map[string]string `json:"files_mapped,omitempty"`
363+
VolumesMapped map[string]*VolumeMapped `json:"volumes_mapped,omitempty"`
364364

365365
Tag string `json:"tag,omitempty"`
366366
Image string `json:"image,omitempty"`
@@ -373,6 +373,11 @@ type Service struct {
373373
release *release
374374
}
375375

376+
type VolumeMapped struct {
377+
Name string
378+
IsLocal bool
379+
}
380+
376381
type DependsOnCondition string
377382

378383
const (
@@ -495,11 +500,18 @@ func (s *Service) WithArgs(args ...string) *Service {
495500
return s
496501
}
497502

498-
func (s *Service) WithVolume(name, localPath string) *Service {
503+
func (s *Service) WithVolume(name, localPath string, isLocalTri ...bool) *Service {
504+
isLocal := false
505+
if len(isLocalTri) == 1 {
506+
isLocal = isLocalTri[0]
507+
}
499508
if s.VolumesMapped == nil {
500-
s.VolumesMapped = make(map[string]string)
509+
s.VolumesMapped = make(map[string]*VolumeMapped)
510+
}
511+
s.VolumesMapped[localPath] = &VolumeMapped{
512+
Name: name,
513+
IsLocal: isLocal,
501514
}
502-
s.VolumesMapped[localPath] = name
503515
return s
504516
}
505517

@@ -751,6 +763,6 @@ func ReadManifest(outputFolder string) (*Manifest, error) {
751763

752764
func (component *Component) RunContenderIfEnabled(ctx *ExContext) {
753765
if ctx.Contender.Enabled {
754-
component.AddService(ctx.Contender.Contender())
766+
component.AddService(ctx, ctx.Contender.Contender())
755767
}
756768
}

playground/recipe_l1.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ type L1Recipe struct {
3434
useNativeReth bool
3535

3636
useSeparateMevBoost bool
37+
38+
withRbuilder bool
3739
}
3840

3941
func (l *L1Recipe) Name() string {
@@ -52,6 +54,7 @@ func (l *L1Recipe) Flags() *flag.FlagSet {
5254
flags.StringVar(&l.secondaryEL, "secondary-el", "", "Address or port to use for the secondary EL (execution layer); Can be a port number (e.g., '8551') in which case the full URL is derived as `http://localhost:<port>` or a complete URL (e.g., `http://docker-container-name:8551`), use `http://host.docker.internal:<port>` to reach a secondary execution client that runs on your host and not within Docker.")
5355
flags.BoolVar(&l.useNativeReth, "use-native-reth", false, "use the native reth binary")
5456
flags.BoolVar(&l.useSeparateMevBoost, "use-separate-mev-boost", false, "use separate mev-boost and mev-boost-relay services")
57+
flags.BoolVar(&l.withRbuilder, "rbuilder", false, "include rbuilder in the recipe")
5558
return flags
5659
}
5760

@@ -134,6 +137,10 @@ func (l *L1Recipe) Apply(ctx *ExContext) *Component {
134137
})
135138
}
136139

140+
if l.withRbuilder {
141+
component.AddComponent(ctx, &Rbuilder{})
142+
}
143+
137144
component.RunContenderIfEnabled(ctx)
138145
return component
139146
}

0 commit comments

Comments
 (0)