Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
6 changes: 3 additions & 3 deletions framework/docker/chain_node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func TestChainNodeHostName(t *testing.T) {
AdditionalStartArgs: []string{},
EncodingConfig: &testutil.TestEncodingConfig{},
}
node1 := cosmos.NewChainNode(logger, &tastoraclient.Client{}, "test-network", testName, container.Image{}, "/test/home", 0, chainParams1)
node1 := cosmos.NewChainNode(logger, &tastoraclient.Client{}, "test-network", testName, container.Image{}, "/test/home", 0, chainParams1) //nolint:staticcheck // testing deprecated API

chainParams2 := cosmos.ChainNodeParams{
Validator: true,
Expand All @@ -43,7 +43,7 @@ func TestChainNodeHostName(t *testing.T) {
AdditionalStartArgs: []string{},
EncodingConfig: &testutil.TestEncodingConfig{},
}
node2 := cosmos.NewChainNode(logger, &tastoraclient.Client{}, "test-network", testName, container.Image{}, "/test/home", 1, chainParams2)
node2 := cosmos.NewChainNode(logger, &tastoraclient.Client{}, "test-network", testName, container.Image{}, "/test/home", 1, chainParams2) //nolint:staticcheck // testing deprecated API

chainParams3 := cosmos.ChainNodeParams{
Validator: false,
Expand All @@ -56,7 +56,7 @@ func TestChainNodeHostName(t *testing.T) {
AdditionalStartArgs: []string{},
EncodingConfig: &testutil.TestEncodingConfig{},
}
node3 := cosmos.NewChainNode(logger, &tastoraclient.Client{}, "test-network", testName, container.Image{}, "/test/home", 2, chainParams3)
node3 := cosmos.NewChainNode(logger, &tastoraclient.Client{}, "test-network", testName, container.Image{}, "/test/home", 2, chainParams3) //nolint:staticcheck // testing deprecated API

// get hostnames
hostname1 := node1.HostName()
Expand Down
82 changes: 82 additions & 0 deletions framework/docker/container/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Node struct {
Logger *zap.Logger
}

// Deprecated: use NewNodeBuilder instead.
// NewNode creates a new Node instance with the required parameters.
func NewNode(
networkID string,
Expand All @@ -53,6 +54,86 @@ func NewNode(
}
}

// NodeBuilder provides a fluent API for constructing and initializing a Node.
type NodeBuilder struct {
networkID string
dockerClient types.TastoraDockerClient
testName string
image Image
homeDir string
index int
nodeType types.NodeType
logger *zap.Logger
volumeName string
hostNetwork bool
}

// NewNodeBuilder creates a NodeBuilder with the minimum required fields.
func NewNodeBuilder(dockerClient types.TastoraDockerClient, testName string, image Image, logger *zap.Logger) *NodeBuilder {
return &NodeBuilder{
dockerClient: dockerClient,
testName: testName,
image: image,
logger: logger,
}
}

func (b *NodeBuilder) WithNetworkID(networkID string) *NodeBuilder {
b.networkID = networkID
return b
}

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

func (b *NodeBuilder) WithIndex(index int) *NodeBuilder {
b.index = index
return b
}

func (b *NodeBuilder) WithNodeType(nodeType types.NodeType) *NodeBuilder {
b.nodeType = nodeType
return b
}

// WithHostNetwork enables host network mode on the container lifecycle.
func (b *NodeBuilder) WithHostNetwork(hostNetwork bool) *NodeBuilder {
b.hostNetwork = hostNetwork
return b
}

// WithVolumeName overrides the name used for volume creation.
// By default, Build uses the containerName for the volume. Use this to share
// a volume with another node (e.g. a deployer and its agent).
func (b *NodeBuilder) WithVolumeName(volumeName string) *NodeBuilder {
b.volumeName = volumeName
return b
}

// Build creates the Node with its lifecycle and volume fully initialized.
// The containerName is used for the lifecycle and (unless WithVolumeName was called)
// for the volume. Callers should pass the name that the embedding type will use
// as its container name.
func (b *NodeBuilder) Build(ctx context.Context, containerName string) (*Node, error) {
n := NewNode(b.networkID, b.dockerClient, b.testName, b.image, b.homeDir, b.index, b.nodeType, b.logger)
lc := NewLifecycle(b.logger, b.dockerClient, containerName)
if b.hostNetwork {
lc.SetHostNetwork(b.hostNetwork)
}
n.ContainerLifecycle = lc
volName := containerName
if b.volumeName != "" {
volName = b.volumeName
}
if err := n.CreateAndSetupVolume(ctx, volName); err != nil {
return nil, fmt.Errorf("setup node %q: %w", containerName, err)
}
return n, nil
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// Deprecated: use NewNodeBuilder instead.
// SetContainerLifecycle sets the container lifecycle for the node
func (n *Node) SetContainerLifecycle(lifecycle *Lifecycle) {
n.ContainerLifecycle = lifecycle
Expand Down Expand Up @@ -164,6 +245,7 @@ func (n *Node) GetVolumeName(nodeName string) string {
return internal.SanitizeDockerResourceName(n.TestName + "-" + nodeName + "-vol")
}

// Deprecated: use NewNodeBuilder instead.
// CreateAndSetupVolume creates a Docker volume for the node and sets up proper ownership.
// This consolidates the volume creation pattern used across all node types.
// The nodeName parameter should be the specific name for this node instance.
Expand Down
61 changes: 37 additions & 24 deletions framework/docker/cosmos/chain_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,26 +482,6 @@ func (b *ChainBuilder) newChainNode(
nodeConfig ChainNodeConfig,
index int,
) (*ChainNode, error) {
// Construct the ChainNode first so we can access its name.
// The ChainNode's VolumeName cannot be set until after we create the volume.
tn := b.newDockerChainNode(b.logger, nodeConfig, index)

// create and setup volume using shared logic
if err := tn.CreateAndSetupVolume(ctx, tn.Name()); err != nil {
return nil, err
}

// if this is a validator and we have a genesis keyring, preload the keys using a one-shot container
if nodeConfig.nodeType == types.NodeTypeValidator && tn.GenesisKeyring != nil {
if err := preloadKeyringToVolume(ctx, tn, nodeConfig); err != nil {
return nil, fmt.Errorf("failed to preload keyring to volume: %w", err)
}
}

return tn, nil
}

func (b *ChainBuilder) newDockerChainNode(log *zap.Logger, nodeConfig ChainNodeConfig, index int) *ChainNode {
homeDir := b.homeDir
if homeDir == "" {
homeDir = "/var/cosmos-chain"
Expand All @@ -510,9 +490,15 @@ func (b *ChainBuilder) newDockerChainNode(log *zap.Logger, nodeConfig ChainNodeC
}
}

// default unspecified node type to full node
nodeType := nodeConfig.nodeType
if nodeType == 0 {
nodeType = types.NodeTypeConsensusFull
}

chainParams := ChainNodeParams{
Validator: nodeConfig.nodeType == types.NodeTypeValidator,
NodeType: nodeConfig.nodeType,
Validator: nodeType == types.NodeTypeValidator,
NodeType: nodeType,
ChainID: b.chainID,
BinaryName: b.binaryName,
CoinType: b.coinType,
Expand All @@ -528,10 +514,37 @@ func (b *ChainBuilder) newDockerChainNode(log *zap.Logger, nodeConfig ChainNodeC
AdditionalExposedPorts: b.additionalExposedPorts,
}

// Get the appropriate image using fallback logic
imageToUse := b.getImage(nodeConfig)

return NewChainNode(log, b.dockerClient, b.dockerNetworkID, b.testName, imageToUse, homeDir, index, chainParams)
log := b.logger.With(
zap.Bool("validator", chainParams.Validator),
zap.Int("i", index),
)

name := chainNodeName(b.testName, index, b.chainID, nodeType)
baseNode, err := container.NewNodeBuilder(b.dockerClient, b.testName, imageToUse, log).
WithNetworkID(b.dockerNetworkID).
WithHomeDir(homeDir).
WithIndex(index).
WithNodeType(nodeType).
Build(ctx, name)
if err != nil {
return nil, err
}

tn := &ChainNode{
ChainNodeParams: chainParams,
Node: baseNode,
}

// if this is a validator and we have a genesis keyring, preload the keys using a one-shot container
if nodeConfig.nodeType == types.NodeTypeValidator && tn.GenesisKeyring != nil {
if err := preloadKeyringToVolume(ctx, tn, nodeConfig); err != nil {
return nil, fmt.Errorf("failed to preload keyring to volume: %w", err)
}
}

return tn, nil
}

// preloadKeyringToVolume copies validator keys from genesis keyring to the node's volume
Expand Down
7 changes: 6 additions & 1 deletion framework/docker/cosmos/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ type ChainNodeParams struct {
AdditionalExposedPorts []string
}

// Deprecated: use container.NewNodeBuilder instead.
// NewChainNode creates a new ChainNode with injected dependencies
func NewChainNode(
logger *zap.Logger,
Expand Down Expand Up @@ -248,9 +249,13 @@ func (cn *ChainNode) GetKeyring() (keyring.Keyring, error) {
return internal.NewDockerKeyring(cn.DockerClient, cn.ContainerLifecycle.ContainerID(), containerKeyringDir, cn.EncodingConfig.Codec), nil
}

func chainNodeName(testName string, index int, chainID string, nodeType types.ConsensusNodeType) string {
return fmt.Sprintf("%s-%s-%d-%s", chainID, nodeType.String(), index, internal.SanitizeDockerResourceName(testName))
}

// Name of the test node container.
func (cn *ChainNode) Name() string {
return fmt.Sprintf("%s-%s-%d-%s", cn.ChainID, cn.NodeType(), cn.Index, internal.SanitizeDockerResourceName(cn.TestName))
return chainNodeName(cn.TestName, cn.Index, cn.ChainID, cn.ChainNodeParams.NodeType)
}

// NodeType returns the type of the ChainNode as a string.
Expand Down
25 changes: 22 additions & 3 deletions framework/docker/dataavailability/network_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,32 @@ func (b *NetworkBuilder) newNode(ctx context.Context, nodeConfig NodeConfig, ind
nodeConfig.Env = b.env
}

node := NewNode(cfg, b.testName, imageToUse, index, nodeConfig)
homeDir := cfg.HomeDir
if homeDir == "" {
homeDir = DefaultHomeDir()
}

// Create and setup volume using shared logic
if err := node.CreateAndSetupVolume(ctx, node.Name()); err != nil {
log := b.logger.With(zap.String("node_type", nodeConfig.NodeType.String()))
name := daNodeName(b.testName, index, nodeConfig.NodeType)
baseNode, err := container.NewNodeBuilder(b.dockerClient, b.testName, imageToUse, log).
WithNetworkID(b.dockerNetworkID).
WithHomeDir(homeDir).
WithIndex(index).
WithNodeType(nodeConfig.NodeType).
Build(ctx, name)
if err != nil {
return nil, err
}

node := &Node{
cfg: cfg,
nodeType: nodeConfig.NodeType,
additionalStartArgs: nodeConfig.AdditionalStartArgs,
configModifications: nodeConfig.ConfigModifications,
internalPorts: initializeDANodePorts(nodeConfig.InternalPorts),
Node: baseNode,
}

// Run post-init functions if any
for _, postInitFn := range nodeConfig.postInit {
if err := postInitFn(ctx, node); err != nil {
Expand Down
7 changes: 6 additions & 1 deletion framework/docker/dataavailability/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type Node struct {
externalPorts types.Ports
}

// Deprecated: use container.NewNodeBuilder instead.
func NewNode(cfg Config, testName string, image container.Image, index int, nodeConfig NodeConfig) *Node {
homeDir := cfg.HomeDir
if homeDir == "" {
Expand All @@ -97,9 +98,13 @@ func NewNode(cfg Config, testName string, image container.Image, index int, node
return node
}

func daNodeName(testName string, index int, nodeType types.DANodeType) string {
return fmt.Sprintf("da-%s-%d-%s", nodeType.String(), index, internal.SanitizeDockerResourceName(testName))
}

// Name returns the container name for the Node.
func (n *Node) Name() string {
return fmt.Sprintf("da-%s-%d-%s", n.nodeType.String(), n.Index, internal.SanitizeDockerResourceName(n.TestName))
return daNodeName(n.TestName, n.Index, n.nodeType)
}

// HostName returns the condensed hostname for the Node.
Expand Down
27 changes: 24 additions & 3 deletions framework/docker/evstack/chain_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,34 @@ func (b *ChainBuilder) newNode(ctx context.Context, nodeConfig NodeConfig, index
HomeDir: b.homeDir,
}

node := NewNode(cfg, b.testName, imageToUse, index, nodeConfig.IsAggregator, b.getAdditionalStartArgs(nodeConfig))
homeDir := cfg.HomeDir
if homeDir == "" {
homeDir = DefaultHomeDir()
}

// Create and setup volume using shared logic
if err := node.CreateAndSetupVolume(ctx, node.Name()); err != nil {
log := b.logger.With(
zap.Int("i", index),
zap.Bool("aggregator", nodeConfig.IsAggregator),
)

name := evstackNodeName(b.testName, index, b.chainID)
baseNode, err := container.NewNodeBuilder(b.dockerClient, b.testName, imageToUse, log).
WithNetworkID(b.dockerNetworkID).
WithHomeDir(homeDir).
WithIndex(index).
WithNodeType(EvstackType).
Build(ctx, name)
if err != nil {
return nil, err
}

node := &Node{
cfg: cfg,
isAggregatorFlag: nodeConfig.IsAggregator,
additionalStartArgs: b.getAdditionalStartArgs(nodeConfig),
Node: baseNode,
}

// Run post-init functions if any
for _, postInitFn := range nodeConfig.postInit {
if err := postInitFn(ctx, node); err != nil {
Expand Down
26 changes: 18 additions & 8 deletions framework/docker/evstack/evmsingle/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,31 @@ 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))

n := &Node{cfg: cfg, nodeCfg: nodeCfg, logger: log, internal: ports, chainName: chainName}
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 {
containerName := evmSingleNodeName(testName, index, chainName)
node, err := container.NewNodeBuilder(cfg.DockerClient, testName, image, log).
WithNetworkID(cfg.DockerNetworkID).
WithHomeDir(cfg.HomeDir).
WithIndex(index).
WithNodeType(NodeType).
Build(ctx, containerName)
if err != nil {
return nil, err
}
n := &Node{cfg: cfg, nodeCfg: nodeCfg, logger: log, internal: ports, chainName: chainName}
n.Node = node
return n, nil
}

func evmSingleNodeName(testName string, index int, chainName string) string {
if chainName != "" {
return fmt.Sprintf("evm-single-%s-%d-%s", chainName, index, internal.SanitizeDockerResourceName(testName))
}
return fmt.Sprintf("evm-single-%d-%s", index, internal.SanitizeDockerResourceName(testName))
}

// Name returns a stable container name
func (n *Node) Name() string {
if n.chainName != "" {
return fmt.Sprintf("evm-single-%s-%d-%s", n.chainName, n.Index, internal.SanitizeDockerResourceName(n.TestName))
}
return fmt.Sprintf("evm-single-%d-%s", n.Index, internal.SanitizeDockerResourceName(n.TestName))
return evmSingleNodeName(n.TestName, n.Index, n.chainName)
}

// HostName returns a condensed hostname
Expand Down
7 changes: 6 additions & 1 deletion framework/docker/evstack/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type Node struct {
externalPorts types.Ports
}

// Deprecated: use container.NewNodeBuilder instead.
func NewNode(cfg Config, testName string, image container.Image, index int, isAggregator bool, additionalStartArgs []string) *Node {
homeDir := cfg.HomeDir
if homeDir == "" {
Expand All @@ -82,9 +83,13 @@ func NewNode(cfg Config, testName string, image container.Image, index int, isAg
return node
}

func evstackNodeName(testName string, index int, chainID string) string {
return fmt.Sprintf("%s-evstack-%d-%s", chainID, index, internal.SanitizeDockerResourceName(testName))
}

// Name of the test node container.
func (n *Node) Name() string {
return fmt.Sprintf("%s-evstack-%d-%s", n.cfg.ChainID, n.Index, internal.SanitizeDockerResourceName(n.TestName))
return evstackNodeName(n.TestName, n.Index, n.cfg.ChainID)
}

// HostName returns the condensed hostname for the Node.
Expand Down
Loading
Loading