Skip to content

Commit 34c2197

Browse files
Remove post-hook feature and move post-hook to mkosi recipe (#402)
The mkosi recipe can now take in the builder registration responsibility off the playground code. This helps us remove the complex and unncessary post-hook feature and get rid of an orchestration problem about host dependencies. It is feasible for future to think of and plan ways to orchestrate containers and host services (binaries, custom services with lifecycle hooks) together, with cross-dependencies at start, but the post-hook feature gets in the way of further improvements (observed from a recent personal experiment). --------- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
1 parent 185d86a commit 34c2197

File tree

9 files changed

+196
-316
lines changed

9 files changed

+196
-316
lines changed

playground/utils/builderhub-config.yaml renamed to custom-recipes/buildernet/mkosi/config/builderhub-config.yaml

File renamed without changes.

custom-recipes/buildernet/mkosi/playground.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ recipe:
3434
env:
3535
BUILDERNET_IMAGE: "https://downloads.buildernet.org/buildernet-playground-images/v2.3.1/buildernet-qemu_2.3.1-46fd6f50.qcow2"
3636
init:
37+
- ./scripts/register-builder.sh
3738
- ./scripts/prepare.sh
3839
start: ./scripts/start.sh
3940
stop:
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
#!/usr/bin/env bash
2+
# Register the VM builder with the BuilderHub
3+
4+
set -eu -o pipefail
5+
6+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7+
8+
echo "register-builder.sh: Running builder registration..."
9+
10+
# Get builder IP from environment or default to 10.0.2.2
11+
HOST_IP=${HOST_IP:-10.0.2.2}
12+
13+
# Default values for the registration
14+
BUILDER_ID="playground_vm_builder"
15+
MEASUREMENT_ID="test1"
16+
NETWORK="playground"
17+
BUILDER_IP="1.2.3.4"
18+
19+
# Read genesis.json generated by the recipe
20+
GENESIS_JSON=$(<"$HOME/.local/state/builder-playground/devnet/genesis.json")
21+
22+
# path to the embedded default (adjust if the file lives elsewhere)
23+
CONFIG_YAML="$SCRIPT_DIR/../config/builderhub-config.yaml"
24+
25+
# Convert YAML to JSON and inject genesis
26+
CONFIG_JSON=$(yq eval -o=json "$CONFIG_YAML") || {
27+
echo "register-builder.sh: Error - invalid YAML in $CONFIG_YAML" >&2
28+
exit 1
29+
}
30+
# Replace the genesis field with the actual genesis JSON
31+
CONFIG_JSON=$(echo "$CONFIG_JSON" | jq --argjson genesis "$GENESIS_JSON" '.genesis = $genesis')
32+
33+
# Function to extract port from manifest
34+
# Usage: extract_port_from_manifest <service_name> <port_name>
35+
extract_port_from_manifest() {
36+
local service_name="$1"
37+
local port_name="$2"
38+
local manifest_file="${MANIFEST_PATH:-$HOME/.local/state/builder-playground/devnet/manifest.json}"
39+
40+
# Check if manifest file exists
41+
if [[ ! -f "$manifest_file" ]]; then
42+
echo "register-builder.sh: Error - manifest file not found at $manifest_file" >&2
43+
exit 1
44+
fi
45+
46+
# Extract port using jq
47+
local port
48+
port=$(jq -r --arg service "$service_name" --arg port "$port_name" \
49+
'.services[] | select(.name == $service) | .ports[] | select(.name == $port) | .port' \
50+
"$manifest_file" 2>/dev/null || echo "null")
51+
52+
# Validate port value
53+
if [[ "$port" = "null" || -z "$port" ]]; then
54+
echo "register-builder.sh: Error - port $port_name not found for service $service_name" >&2
55+
exit 1
56+
fi
57+
58+
echo "$port"
59+
}
60+
61+
# Get ports from manifest dynamically
62+
echo "register-builder.sh: Reading ports from manifest..."
63+
64+
# Use known port names for services - these are consistent across deployments
65+
# builder-hub-api uses 'admin' port for admin API
66+
BUILDER_ADMIN_API_PORT=$(extract_port_from_manifest "builder-hub-api" "admin")
67+
echo "register-builder.sh: BUILDER_ADMIN_API_PORT = $BUILDER_ADMIN_API_PORT"
68+
69+
# beacon service - use 'http' port for beacon API calls
70+
BEACON_API_PORT=$(extract_port_from_manifest "beacon" "http")
71+
echo "register-builder.sh: BEACON_API_PORT = $BEACON_API_PORT"
72+
73+
# el service - use 'http' port for JSON-RPC calls
74+
RETH_API_PORT=$(extract_port_from_manifest "el" "http")
75+
echo "register-builder.sh: RETH_API_PORT = $RETH_API_PORT"
76+
77+
# Get the beacon node identity
78+
echo "register-builder.sh: Getting beacon node identity..."
79+
BEACON_IDENTITY_URL="http://localhost:${BEACON_API_PORT}/eth/v1/node/identity"
80+
IDENTITY_RESPONSE=$(curl -sf "$BEACON_IDENTITY_URL") || {
81+
echo "register-builder.sh: Error getting beacon node identity"
82+
exit 1
83+
}
84+
85+
# Extract peer ID from the identity response
86+
PEER_ID=$(echo "$IDENTITY_RESPONSE" | jq -r '.data.peer_id') || {
87+
echo "register-builder.sh: Error extracting peer ID from beacon node identity"
88+
exit 1
89+
}
90+
if [ "$PEER_ID" = "null" ] || [ -z "$PEER_ID" ]; then
91+
echo "register-builder.sh: Error extracting peer ID from beacon node identity"
92+
exit 1
93+
fi
94+
95+
# Construct libp2p address with the known host IP
96+
LIBP2P_ADDR="/ip4/${HOST_IP}/tcp/9001/p2p/${PEER_ID}"
97+
echo "register-builder.sh: Setting builder config var libp2p-addr: ${LIBP2P_ADDR}"
98+
99+
# Get reth node info to extract enode
100+
echo "register-builder.sh: Getting reth node info..."
101+
RETH_NODE_INFO_URL="http://localhost:${RETH_API_PORT}/"
102+
RETH_RESPONSE=$(curl -sf -X POST \
103+
-H "Content-Type: application/json" \
104+
-d '{"jsonrpc":"2.0","method":"admin_nodeInfo","id":1}' \
105+
"$RETH_NODE_INFO_URL") || {
106+
echo "register-builder.sh: Error getting reth node info"
107+
exit 1
108+
}
109+
110+
# Extract enode from response
111+
ENODE=$(echo "$RETH_RESPONSE" | jq -r '.result.enode') || {
112+
echo "register-builder.sh: Error extracting enode from reth node info"
113+
exit 1
114+
}
115+
if [ "$ENODE" = "null" ] || [ -z "$ENODE" ]; then
116+
echo "register-builder.sh: Error extracting enode from reth node info"
117+
exit 1
118+
fi
119+
120+
# Replace the IP address in the enode with the host IP
121+
BOOT_NODE=$(echo "$ENODE" | sed "s|@[^:]*:|@${HOST_IP}:|")
122+
echo "register-builder.sh: Setting builder config var bootnode: ${BOOT_NODE}"
123+
124+
# Replace template variables in the JSON config
125+
CONFIG="$CONFIG_JSON"
126+
CONFIG=$(echo "$CONFIG" | sed "s|{{EL_BOOTNODE}}|${BOOT_NODE}|g")
127+
CONFIG=$(echo "$CONFIG" | sed "s|{{CL_LIBP2P_ADDR}}|${LIBP2P_ADDR}|g")
128+
129+
echo "register-builder.sh: Registering builder with BuilderHub..."
130+
131+
# Create Allow-All Measurements
132+
echo "register-builder.sh: Creating measurements..."
133+
curl -sf -X POST \
134+
-H "Content-Type: application/json" \
135+
-d "{\"measurement_id\":\"$MEASUREMENT_ID\",\"attestation_type\":\"test\",\"measurements\":{}}" \
136+
"http://localhost:${BUILDER_ADMIN_API_PORT}/api/admin/v1/measurements" || {
137+
echo "register-builder.sh: Error creating measurements"
138+
exit 1
139+
}
140+
141+
# Enable Measurements
142+
echo "register-builder.sh: Enabling measurements..."
143+
curl -sf -X POST \
144+
-H "Content-Type: application/json" \
145+
-d "{\"enabled\":true}" \
146+
"http://localhost:${BUILDER_ADMIN_API_PORT}/api/admin/v1/measurements/activation/$MEASUREMENT_ID" || {
147+
echo "register-builder.sh: Error enabling measurements"
148+
exit 1
149+
}
150+
151+
# Create the builder
152+
echo "register-builder.sh: Creating builder..."
153+
curl -sf -X POST \
154+
-H "Content-Type: application/json" \
155+
-d "{\"name\":\"$BUILDER_ID\",\"ip_address\":\"$BUILDER_IP\",\"network\":\"$NETWORK\"}" \
156+
"http://localhost:${BUILDER_ADMIN_API_PORT}/api/admin/v1/builders" || {
157+
echo "register-builder.sh: Error creating builder"
158+
exit 1
159+
}
160+
161+
# Create Builder Configuration
162+
echo "register-builder.sh: Setting builder configuration..."
163+
curl -sf -X POST \
164+
-H "Content-Type: application/json" \
165+
-d "$CONFIG" \
166+
"http://localhost:${BUILDER_ADMIN_API_PORT}/api/admin/v1/builders/configuration/$BUILDER_ID" || {
167+
echo "register-builder.sh: Error setting builder configuration"
168+
exit 1
169+
}
170+
171+
# Create Builder Secrets (using same config for secrets in this case)
172+
echo "register-builder.sh: Setting builder secrets..."
173+
curl -sf -X POST \
174+
-H "Content-Type: application/json" \
175+
-d "$CONFIG" \
176+
"http://localhost:${BUILDER_ADMIN_API_PORT}/api/admin/v1/builders/secrets/$BUILDER_ID" || {
177+
echo "register-builder.sh: Error setting builder secrets"
178+
exit 1
179+
}
180+
181+
# Enable Builder
182+
echo "register-builder.sh: Enabling builder..."
183+
curl -sf -X POST \
184+
-H "Content-Type: application/json" \
185+
-d "{\"enabled\":true}" \
186+
"http://localhost:${BUILDER_ADMIN_API_PORT}/api/admin/v1/builders/activation/$BUILDER_ID" || {
187+
echo "register-builder.sh: Error enabling builder"
188+
exit 1
189+
}
190+
191+
echo "register-builder.sh: Builder registration completed successfully!"

docs/recipes/buildernet.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ Deploy a full L1 stack with mev-boost and builder-hub.
55
## Flags
66

77
- `block-time` (duration): Block time to use for the L1. Default to '12s'.
8-
- `builder-config` (string): Builder config in YAML format. Default to ''.
9-
- `builder-ip` (string): IP address of the external builder to register in BuilderHub. Default to '127.0.0.1'.
108
- `latest-fork` (bool): use the latest fork. Default to 'false'.
119
- `rbuilder` (bool): include rbuilder in the recipe. Default to 'false'.
1210
- `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 ''.

main.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -791,12 +791,6 @@ func runIt(recipe playground.Recipe) error {
791791
return fmt.Errorf("failed to wait for service readiness: %w", err)
792792
}
793793

794-
// run post hook operations
795-
if err := svcManager.ExecutePostHookActions(ctx); err != nil {
796-
dockerRunner.Stop(keepFlag)
797-
return fmt.Errorf("failed to execute post-hook operations: %w", err)
798-
}
799-
800794
slog.Info("Running lifecycle hooks of services... ⏳")
801795
if err := dockerRunner.RunLifecycleHooks(ctx); err != nil {
802796
dockerRunner.Stop(keepFlag)

playground/artifacts.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,6 @@ var opStateJovian []byte
6868
//go:embed config.yaml.tmpl
6969
var clConfigContent []byte
7070

71-
//go:embed utils/builderhub-config.yaml
72-
var defaultBuilderHubConfig []byte
73-
7471
// l2ForkConfig holds the selected L2 fork configuration files
7572
type l2ForkConfig struct {
7673
genesis []byte // L2 genesis JSON

playground/components.go

Lines changed: 1 addition & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package playground
22

33
import (
4-
"context"
54
_ "embed"
65
"fmt"
7-
"os"
86
"strconv"
97
"strings"
108
"time"
@@ -13,7 +11,6 @@ import (
1311
mevboostrelay "github.com/flashbots/builder-playground/mev-boost-relay"
1412
"github.com/flashbots/go-boost-utils/bls"
1513
"github.com/flashbots/go-boost-utils/utils"
16-
"github.com/goccy/go-yaml"
1714
)
1815

1916
var (
@@ -935,10 +932,7 @@ func (c *Contender) Apply(ctx *ExContext) *Component {
935932
return component
936933
}
937934

938-
type BuilderHub struct {
939-
BuilderIP string
940-
BuilderConfig string
941-
}
935+
type BuilderHub struct{}
942936

943937
func (b *BuilderHub) Apply(exCtx *ExContext) *Component {
944938
component := NewComponent("builder-hub")
@@ -983,12 +977,6 @@ func (b *BuilderHub) Apply(exCtx *ExContext) *Component {
983977
Timeout: 30 * time.Second,
984978
Retries: 3,
985979
StartPeriod: 1 * time.Second,
986-
}).
987-
WithPostHook(&postHook{
988-
Name: "register-builder",
989-
Action: func(ctx context.Context, m *Manifest, s *Service) error {
990-
return registerBuilderHook(ctx, exCtx, m, s, b)
991-
},
992980
})
993981

994982
// Proxy service
@@ -1009,65 +997,6 @@ func (b *BuilderHub) Apply(exCtx *ExContext) *Component {
1009997
return component
1010998
}
1011999

1012-
type builderHubConfig struct {
1013-
Playground struct {
1014-
BuilderHubConfig struct {
1015-
BuilderID string `yaml:"builder_id"`
1016-
BuilderIP string `yaml:"builder_ip"`
1017-
MeasurementID string `yaml:"measurement_id"`
1018-
Network string `yaml:"network"`
1019-
} `yaml:"builder_hub_config"`
1020-
} `yaml:"playground"`
1021-
}
1022-
1023-
func registerBuilderHook(ctx context.Context, exCtx *ExContext, manifest *Manifest, s *Service, b *BuilderHub) error {
1024-
genesis, err := exCtx.Output.Read("genesis.json")
1025-
if err != nil {
1026-
return err
1027-
}
1028-
1029-
configYaml := defaultBuilderHubConfig
1030-
if len(b.BuilderConfig) > 0 {
1031-
configYaml, err = os.ReadFile(b.BuilderConfig)
1032-
if err != nil {
1033-
return err
1034-
}
1035-
}
1036-
1037-
var config builderHubConfig
1038-
if err := yaml.Unmarshal([]byte(configYaml), &config); err != nil {
1039-
return err
1040-
}
1041-
1042-
// we need to convert the config to JSON because it is what the API server accepts
1043-
configJSON, err := yamlToJson([]byte(configYaml))
1044-
if err != nil {
1045-
return err
1046-
}
1047-
1048-
overrideConfig := map[string]interface{}{
1049-
"genesis": genesis,
1050-
}
1051-
if configJSON, err = overrideJSON(configJSON, overrideConfig); err != nil {
1052-
return err
1053-
}
1054-
1055-
input := &builderHubRegisterBuilderInput{
1056-
BuilderID: config.Playground.BuilderHubConfig.BuilderID,
1057-
BuilderIP: config.Playground.BuilderHubConfig.BuilderIP,
1058-
MeasurementID: config.Playground.BuilderHubConfig.MeasurementID,
1059-
Network: config.Playground.BuilderHubConfig.Network,
1060-
Config: string(configJSON),
1061-
}
1062-
adminApi := fmt.Sprintf("http://localhost:%d", manifest.MustGetService("builder-hub-api").MustGetPort("admin").HostPort)
1063-
beaconApi := fmt.Sprintf("http://localhost:%d", manifest.MustGetService("beacon").MustGetPort("http").HostPort)
1064-
rethApi := fmt.Sprintf("http://localhost:%d", manifest.MustGetService("el").MustGetPort("http").HostPort)
1065-
if err := registerBuilder(ctx, adminApi, beaconApi, rethApi, input); err != nil {
1066-
return err
1067-
}
1068-
return nil
1069-
}
1070-
10711000
type Bootnode struct {
10721001
Enode *EnodeAddr
10731002
}

playground/manifest.go

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package playground
22

33
import (
4-
"context"
54
"encoding/json"
65
"fmt"
7-
"log/slog"
86
"os"
97
"path/filepath"
108
"strings"
@@ -448,8 +446,7 @@ type Service struct {
448446
// RecipeDir is the directory containing the recipe file (for lifecycle hooks)
449447
RecipeDir string `json:"recipe_dir,omitempty"`
450448

451-
postHook *postHook
452-
release *release
449+
release *release
453450
}
454451

455452
type VolumeMapped struct {
@@ -620,29 +617,6 @@ func (s *Service) WithReady(check ReadyCheck) *Service {
620617
return s
621618
}
622619

623-
type postHook struct {
624-
Name string
625-
Action func(ctx context.Context, m *Manifest, s *Service) error
626-
}
627-
628-
func (s *Service) WithPostHook(hook *postHook) *Service {
629-
s.postHook = hook
630-
return s
631-
}
632-
633-
func (m *Manifest) ExecutePostHookActions(ctx context.Context) error {
634-
for _, svc := range m.Services {
635-
if svc.postHook != nil {
636-
slog.Info("Executing post-hook operation", "name", svc.postHook.Name)
637-
if err := svc.postHook.Action(ctx, m, svc); err != nil {
638-
return err
639-
}
640-
}
641-
}
642-
643-
return nil
644-
}
645-
646620
type ReadyCheck struct {
647621
QueryURL string `json:"query_url"`
648622
Test []string `json:"test"`

0 commit comments

Comments
 (0)