Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions framework/docker/cosmos/chain_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ type ChainBuilder struct {
additionalExposedPorts []string
// skipInit indicates whether to skip node initialization on start (useful when reusing volumes)
skipInit bool
// homeDir overrides the default home directory inside the container
homeDir string
}

// NewChainBuilder initializes and returns a new ChainBuilder with default values for testing purposes.
Expand Down Expand Up @@ -311,6 +313,12 @@ func (b *ChainBuilder) WithImage(image container.Image) *ChainBuilder {
return b
}

// WithHomeDir overrides the default home directory inside the container.
func (b *ChainBuilder) WithHomeDir(homeDir string) *ChainBuilder {
b.homeDir = homeDir
return b
}

// WithAdditionalStartArgs sets the default additional start arguments for all nodes in the chain
func (b *ChainBuilder) WithAdditionalStartArgs(args ...string) *ChainBuilder {
b.additionalStartArgs = args
Expand Down Expand Up @@ -481,10 +489,12 @@ func (b *ChainBuilder) newChainNode(
}

func (b *ChainBuilder) newDockerChainNode(log *zap.Logger, nodeConfig ChainNodeConfig, index int) *ChainNode {
// use a default home directory if name is not set
homeDir := "/var/cosmos-chain"
if b.name != "" {
homeDir = path.Join("/var/cosmos-chain", b.name)
homeDir := b.homeDir
if homeDir == "" {
homeDir = "/var/cosmos-chain"
if b.name != "" {
homeDir = path.Join("/var/cosmos-chain", b.name)
}
}

chainParams := ChainNodeParams{
Expand Down
5 changes: 5 additions & 0 deletions framework/docker/dataavailability/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ type Config struct {
// AdditionalStartArgs are additional arguments passed to all nodes when starting
AdditionalStartArgs []string
}

// DefaultHomeDir returns the default home directory for DA node containers.
func DefaultHomeDir() string {
return "/home/celestia"
}
10 changes: 9 additions & 1 deletion framework/docker/dataavailability/network_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type NetworkBuilder struct {
chainID string
// binaryName is the name of the Node binary executable (e.g., "celestia")
binaryName string
// homeDir overrides the default home directory inside the container
homeDir string
}

// NewNetworkBuilder initializes and returns a new NetworkBuilder with default values for testing purposes
Expand Down Expand Up @@ -108,6 +110,12 @@ func (b *NetworkBuilder) WithDockerNetworkID(networkID string) *NetworkBuilder {
return b
}

// WithHomeDir overrides the default home directory inside the container.
func (b *NetworkBuilder) WithHomeDir(homeDir string) *NetworkBuilder {
b.homeDir = homeDir
return b
}

// WithImage sets the default Docker image for all nodes in the network
func (b *NetworkBuilder) WithImage(image container.Image) *NetworkBuilder {
b.dockerImage = &image
Expand Down Expand Up @@ -228,7 +236,7 @@ func (b *NetworkBuilder) newNode(ctx context.Context, nodeConfig NodeConfig, ind
nodeConfig.Env = b.env
}

node := NewNode(cfg, b.testName, imageToUse, index, nodeConfig)
node := NewNode(cfg, b.testName, imageToUse, b.homeDir, index, nodeConfig)

// Create and setup volume using shared logic
if err := node.CreateAndSetupVolume(ctx, node.Name()); err != nil {
Expand Down
7 changes: 5 additions & 2 deletions framework/docker/dataavailability/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ type Node struct {
externalPorts types.Ports
}

func NewNode(cfg Config, testName string, image container.Image, index int, nodeConfig NodeConfig) *Node {
func NewNode(cfg Config, testName string, image container.Image, homeDir string, index int, nodeConfig NodeConfig) *Node {
if homeDir == "" {
homeDir = DefaultHomeDir()
}
logger := cfg.Logger.With(
zap.String("node_type", nodeConfig.NodeType.String()),
)
Expand All @@ -86,7 +89,7 @@ func NewNode(cfg Config, testName string, image container.Image, index int, node
additionalStartArgs: nodeConfig.AdditionalStartArgs,
configModifications: nodeConfig.ConfigModifications,
internalPorts: initializeDANodePorts(nodeConfig.InternalPorts),
Node: container.NewNode(cfg.DockerNetworkID, cfg.DockerClient, testName, image, "/home/celestia", index, nodeConfig.NodeType, logger),
Node: container.NewNode(cfg.DockerNetworkID, cfg.DockerClient, testName, image, homeDir, index, nodeConfig.NodeType, logger),
}

node.SetContainerLifecycle(container.NewLifecycle(cfg.Logger, cfg.DockerClient, node.Name()))
Expand Down
10 changes: 9 additions & 1 deletion framework/docker/evstack/chain_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ type ChainBuilder struct {
binaryName string
// aggregatorPassphrase is the passphrase used for aggregator nodes
aggregatorPassphrase string
// homeDir overrides the default home directory inside the container
homeDir string
}

// NewChainBuilder initializes and returns a new ChainBuilder with default values for testing purposes
Expand Down Expand Up @@ -101,6 +103,12 @@ func (b *ChainBuilder) WithDockerNetworkID(networkID string) *ChainBuilder {
return b
}

// WithHomeDir overrides the default home directory inside the container.
func (b *ChainBuilder) WithHomeDir(homeDir string) *ChainBuilder {
b.homeDir = homeDir
return b
}

// WithImage sets the default Docker image for all nodes in the chain
func (b *ChainBuilder) WithImage(image container.Image) *ChainBuilder {
b.dockerImage = &image
Expand Down Expand Up @@ -187,7 +195,7 @@ func (b *ChainBuilder) newNode(ctx context.Context, nodeConfig NodeConfig, index
Image: imageToUse,
}

node := NewNode(cfg, b.testName, imageToUse, index, nodeConfig.IsAggregator, b.getAdditionalStartArgs(nodeConfig))
node := NewNode(cfg, b.testName, imageToUse, b.homeDir, index, nodeConfig.IsAggregator, b.getAdditionalStartArgs(nodeConfig))

// Create and setup volume using shared logic
if err := node.CreateAndSetupVolume(ctx, node.Name()); err != nil {
Expand Down
5 changes: 5 additions & 0 deletions framework/docker/evstack/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ type Config struct {
// Image specifies the Docker image used for the evstack nodes.
Image container.Image
}

// DefaultHomeDir returns the default home directory for evstack containers.
func DefaultHomeDir() string {
return "/var/evstack"
}
12 changes: 12 additions & 0 deletions framework/docker/evstack/evmsingle/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type ChainBuilder struct {
addlArgs []string
nodes []NodeConfig
name string
homeDir string
}

func NewChainBuilder(t *testing.T) *ChainBuilder {
Expand Down Expand Up @@ -100,6 +101,11 @@ func (b *ChainBuilder) WithNodes(cfgs ...NodeConfig) *ChainBuilder {
return b
}

func (b *ChainBuilder) WithHomeDir(homeDir string) *ChainBuilder {
b.homeDir = homeDir
return b
}

func (b *ChainBuilder) WithName(name string) *ChainBuilder {
if err := internal.ValidateDockerHostnamePart(name); err != nil {
panic(fmt.Sprintf("invalid evmsingle chain name: %v", err))
Expand All @@ -110,12 +116,18 @@ func (b *ChainBuilder) WithName(name string) *ChainBuilder {

// Build constructs a Chain with nodes created and volumes initialized (not isInitialized)
func (b *ChainBuilder) Build(ctx context.Context) (*Chain, error) {
homeDir := b.homeDir
if homeDir == "" {
homeDir = DefaultHomeDir()
}

cfg := Config{
Logger: b.logger,
DockerClient: b.dockerClient,
DockerNetworkID: b.networkID,
Image: b.image,
Bin: b.bin,
HomeDir: homeDir,
Env: b.env,
AdditionalStartArgs: b.addlArgs,
}
Expand Down
10 changes: 8 additions & 2 deletions framework/docker/evstack/evmsingle/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type Config struct {
Image container.Image
// Bin is the executable name (default: evm-single)
Bin string
// HomeDir is the home directory inside the container. Defaults to DefaultHomeDir().
HomeDir string
// Env are default environment variables applied to all nodes
Env []string
// AdditionalStartArgs are default start arguments applied to all nodes
Expand All @@ -23,10 +25,14 @@ type Config struct {
AdditionalInitArgs []string
}

// DefaultHomeDir returns the default home directory for ev-node-evm containers.
func DefaultHomeDir() string {
return "/home/ev-node/.evm"
}

// DefaultImage returns the default container image for ev-node-evm.
func DefaultImage() container.Image {
// Default ev-node tag pinned for reproducibility
return container.Image{Repository: "ghcr.io/evstack/ev-node-evm", Version: "v1.0.0-rc.4"}
return container.Image{Repository: "ghcr.io/evstack/ev-node-evm", Version: "v1.0.0-rc.4", UIDGID: "10001:10001"}
}
Comment on lines 34 to 36
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if UIDGID field is actually used when creating containers
rg -n "UIDGID" --type=go -C3

Repository: celestiaorg/tastora

Length of output: 13996


🏁 Script executed:

#!/bin/bash
# Check CreateContainer implementation to see if user is set
ast-grep --pattern $'func ($_ $_) CreateContainer($$$) $_ {
  $$$
}'

Repository: celestiaorg/tastora

Length of output: 6584


🏁 Script executed:

#!/bin/bash
# Find SetVolumeOwner implementation
rg -n "SetVolumeOwner" --type=go -A 10

Repository: celestiaorg/tastora

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Check if User field is set anywhere in container creation
rg -n "\.User\s*=" --type=go framework/docker/container/

Repository: celestiaorg/tastora

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Check container.Config and container.HostConfig struct usage more thoroughly
rg -n "container\.Config|container\.HostConfig" --type=go -A 3 framework/docker/container/lifecycle.go | head -60

Repository: celestiaorg/tastora

Length of output: 272


🏁 Script executed:

#!/bin/bash
# Search for SetVolumeOwner more broadly
rg -n "SetVolumeOwner" --type=go

Repository: celestiaorg/tastora

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Check what SetVolumeOwner does - search for the full implementation
rg -n "func.*SetVolumeOwner" --type=go -A 15

Repository: celestiaorg/tastora

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Look at the complete container.Config struct creation in lifecycle.go
sed -n '94,122p' framework/docker/container/lifecycle.go

Repository: celestiaorg/tastora

Length of output: 735


🏁 Script executed:

#!/bin/bash
# Search for where UidGid is used in node.go
rg -n "UidGid" --type=go

Repository: celestiaorg/tastora

Length of output: 265


🏁 Script executed:

#!/bin/bash
# Get the full context around line 183 in node.go
sed -n '175,195p' framework/docker/container/node.go

Repository: celestiaorg/tastora

Length of output: 677


🏁 Script executed:

#!/bin/bash
# Look at the SetOwner implementation to see what it does
rg -n "func.*SetOwner" --type=go -A 20

Repository: celestiaorg/tastora

Length of output: 1485


🏁 Script executed:

#!/bin/bash
# Check the volume/owner.go file to understand what SetOwner does
cat -n framework/docker/volume/owner.go

Repository: celestiaorg/tastora

Length of output: 3613


🏁 Script executed:

#!/bin/bash
# Search for how other projects set the User field in container config
rg -n "User:" --type=go framework/docker/

Repository: celestiaorg/tastora

Length of output: 665


🏁 Script executed:

#!/bin/bash
# Check job.go to understand when User field is set
sed -n '170,185p' framework/docker/container/job.go

Repository: celestiaorg/tastora

Length of output: 470


🏁 Script executed:

#!/bin/bash
# Check the Job struct and its CreateContainer method
rg -n "type Job struct" --type=go -A 10 framework/docker/container/job.go

Repository: celestiaorg/tastora

Length of output: 283


🏁 Script executed:

#!/bin/bash
# Verify if job.go CreateContainer is used for the main application or for utilities
rg -n "job\.CreateContainer\|\.CreateJobContainer" --type=go | head -20

Repository: celestiaorg/tastora

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Check if there's any documentation or comments about the upstream image default user
rg -n "ev-node-evm\|ev-node" --type=go framework/docker/evstack/ -B 2 -A 2

Repository: celestiaorg/tastora

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Verify: in lifecycle.go, the container.Config does NOT have User field set anywhere
# Let me get the full CreateContainer function
cat -n framework/docker/container/lifecycle.go | sed -n '45,129p'

Repository: celestiaorg/tastora

Length of output: 2537


The container does not actually run as UID 10001:10001 - the User field is missing from the container configuration.

The UIDGID: "10001:10001" only configures volume ownership (via SetOwner()), not the container's runtime user. The CreateContainer method in framework/docker/container/lifecycle.go does not set the User field in container.Config, so the container runs as whatever default user the upstream image specifies.

This creates a critical mismatch: if the image doesn't default to UID 10001, the volume (owned by 10001:10001) will have permission issues when the container writes files as a different user. Additionally, if the image defaults to root, you'd be running as root unnecessarily.

Either set User: image.UIDGID in the container.Config (lines 96-104 of lifecycle.go), or document that the upstream image ghcr.io/evstack/ev-node-evm:v1.0.0-rc.4 is already configured to run as UID 10001 by default.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/docker/evstack/evmsingle/config.go` around lines 27 - 29,
DefaultImage sets UIDGID to "10001:10001" but the container runtime user isn't
set, causing permission mismatches; update CreateContainer to set
container.Config.User = image.UIDGID (where image is the container.Image passed
into CreateContainer) so the runtime user matches the volume owner configured by
SetOwner(), ensuring DefaultImage(), UIDGID, CreateContainer, container.Config
and SetOwner are consistent; alternatively document in DefaultImage that the
upstream image already runs as UID 10001 if you choose not to set User
programmatically.


// DefaultBinary returns the default binary name for ev-node-evm.
Expand Down
5 changes: 1 addition & 4 deletions framework/docker/evstack/evmsingle/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,8 @@ func newNode(ctx context.Context, cfg Config, testName string, index int, nodeCf

log := cfg.Logger.With(zap.String("component", "evm-single"), zap.Int("i", index))

// ev-node-evm default home
homeDir := "/root/.evm"

n := &Node{cfg: cfg, nodeCfg: nodeCfg, logger: log, internal: ports, chainName: chainName}
n.Node = container.NewNode(cfg.DockerNetworkID, cfg.DockerClient, testName, image, homeDir, index, NodeType, log)
n.Node = container.NewNode(cfg.DockerNetworkID, cfg.DockerClient, testName, image, cfg.HomeDir, index, NodeType, log)
n.SetContainerLifecycle(container.NewLifecycle(cfg.Logger, cfg.DockerClient, n.Name()))
if err := n.CreateAndSetupVolume(ctx, n.Name()); err != nil {
return nil, err
Expand Down
8 changes: 5 additions & 3 deletions framework/docker/evstack/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"net/http"
"path"
"path/filepath"
"sync"
"time"
Expand Down Expand Up @@ -54,7 +53,10 @@ type Node struct {
externalPorts types.Ports
}

func NewNode(cfg Config, testName string, image container.Image, index int, isAggregator bool, additionalStartArgs []string) *Node {
func NewNode(cfg Config, testName string, image container.Image, homeDir string, index int, isAggregator bool, additionalStartArgs []string) *Node {
if homeDir == "" {
homeDir = DefaultHomeDir()
}
logger := cfg.Logger.With(
zap.Int("i", index),
zap.Bool("aggregator", isAggregator),
Expand All @@ -63,7 +65,7 @@ func NewNode(cfg Config, testName string, image container.Image, index int, isAg
cfg: cfg,
isAggregatorFlag: isAggregator,
additionalStartArgs: additionalStartArgs,
Node: container.NewNode(cfg.DockerNetworkID, cfg.DockerClient, testName, image, path.Join("/var", "evstack"), index, EvstackType, logger),
Node: container.NewNode(cfg.DockerNetworkID, cfg.DockerClient, testName, image, homeDir, index, EvstackType, logger),
}

node.SetContainerLifecycle(container.NewLifecycle(cfg.Logger, cfg.DockerClient, node.Name()))
Expand Down
12 changes: 12 additions & 0 deletions framework/docker/evstack/reth/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type NodeBuilder struct {
genesis []byte
jwtSecretHex string
name string
homeDir string
hyperlaneChainName string
hyperlaneChainID uint64
hyperlaneDomainID uint32
Expand Down Expand Up @@ -104,6 +105,11 @@ func (b *NodeBuilder) WithName(name string) *NodeBuilder {
return b
}

func (b *NodeBuilder) WithHomeDir(homeDir string) *NodeBuilder {
b.homeDir = homeDir
return b
}

func (b *NodeBuilder) WithHyperlaneChainName(name string) *NodeBuilder {
b.hyperlaneChainName = name
return b
Expand All @@ -121,12 +127,18 @@ func (b *NodeBuilder) WithHyperlaneDomainID(domainID uint32) *NodeBuilder {

// Build constructs the Node and initializes its Docker volume but does not start the container.
func (b *NodeBuilder) Build(ctx context.Context) (*Node, error) {
homeDir := b.homeDir
if homeDir == "" {
homeDir = DefaultHomeDir()
}

cfg := Config{
Logger: b.logger,
DockerClient: b.dockerClient,
DockerNetworkID: b.networkID,
Image: b.image,
Bin: b.bin,
HomeDir: homeDir,
Env: b.env,
AdditionalStartArgs: b.additionalStartArgs,
JWTSecretHex: b.jwtSecretHex,
Expand Down
7 changes: 7 additions & 0 deletions framework/docker/evstack/reth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type Config struct {
Image container.Image
// Bin is the executable name (default: ev-reth)
Bin string
// HomeDir is the home directory inside the container. Defaults to DefaultHomeDir().
HomeDir string

// Env are default environment variables applied to all nodes
Env []string
Expand Down Expand Up @@ -51,6 +53,11 @@ func (c Config) Validate() error {
return nil
}

// DefaultHomeDir returns the default home directory for Reth containers.
func DefaultHomeDir() string {
return "/home/ev-reth"
}

// DefaultImage returns the default container image for Reth nodes.
func DefaultImage() container.Image {
// Pin default to a known stable release for local/E2E reproducibility
Expand Down
3 changes: 1 addition & 2 deletions framework/docker/evstack/reth/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,12 @@ func newNode(ctx context.Context, cfg Config, testName string, index int, name s

log := cfg.Logger.With(zap.String("component", "reth-node"), zap.Int("i", index))

homeDir := "/home/ev-reth"
n := &Node{
cfg: cfg,
logger: log,
name: name,
}
n.Node = container.NewNode(cfg.DockerNetworkID, cfg.DockerClient, testName, image, homeDir, index, NodeType, log)
n.Node = container.NewNode(cfg.DockerNetworkID, cfg.DockerClient, testName, image, cfg.HomeDir, index, NodeType, log)
n.SetContainerLifecycle(container.NewLifecycle(cfg.Logger, cfg.DockerClient, n.Name()))

if err := n.CreateAndSetupVolume(ctx, n.Name()); err != nil {
Expand Down
8 changes: 5 additions & 3 deletions framework/docker/evstack/spamoor/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Builder struct {
dockerNetwork string
logger *zap.Logger
image container.Image
homeDir string

rpcHosts []string
privKey string
Expand All @@ -30,8 +31,9 @@ func NewNodeBuilder(testName string) *Builder {
func (b *Builder) WithDockerClient(c types.TastoraDockerClient) *Builder { b.dockerClient = c; return b }
func (b *Builder) WithDockerNetworkID(id string) *Builder { b.dockerNetwork = id; return b }
func (b *Builder) WithLogger(l *zap.Logger) *Builder { b.logger = l; return b }
func (b *Builder) WithImage(img container.Image) *Builder { b.image = img; return b }
func (b *Builder) WithRPCHosts(hosts ...string) *Builder { b.rpcHosts = hosts; return b }
func (b *Builder) WithImage(img container.Image) *Builder { b.image = img; return b }
func (b *Builder) WithHomeDir(homeDir string) *Builder { b.homeDir = homeDir; return b }
func (b *Builder) WithRPCHosts(hosts ...string) *Builder { b.rpcHosts = hosts; return b }
func (b *Builder) WithPrivateKey(pk string) *Builder { b.privKey = pk; return b }
func (b *Builder) WithNameSuffix(s string) *Builder { b.nameSuffix = s; return b }

Expand All @@ -44,6 +46,6 @@ func (b *Builder) Build(ctx context.Context) (*Node, error) {
RPCHosts: b.rpcHosts,
PrivateKey: b.privKey,
}
return newNode(ctx, cfg, b.testName, 0, b.nameSuffix)
return newNode(ctx, cfg, b.testName, 0, b.nameSuffix, b.homeDir)
}

7 changes: 5 additions & 2 deletions framework/docker/evstack/spamoor/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,13 @@ type Node struct {
name string
}

func newNode(ctx context.Context, cfg Config, testName string, index int, name string) (*Node, error) {
func newNode(ctx context.Context, cfg Config, testName string, index int, name string, homeDir string) (*Node, error) {
if homeDir == "" {
homeDir = "/home/spamoor"
}
log := cfg.Logger.With(zap.String("component", "spamoor-daemon"), zap.Int("i", index))
n := &Node{cfg: cfg, logger: log, name: name}
n.Node = container.NewNode(cfg.DockerNetworkID, cfg.DockerClient, testName, cfg.Image, "/home/spamoor", index, nodeType(0), log)
n.Node = container.NewNode(cfg.DockerNetworkID, cfg.DockerClient, testName, cfg.Image, homeDir, index, nodeType(0), log)
n.SetContainerLifecycle(container.NewLifecycle(cfg.Logger, cfg.DockerClient, n.Name()))
if err := n.CreateAndSetupVolume(ctx, n.Name()); err != nil {
return nil, err
Expand Down
Loading