Skip to content
Merged
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
5 changes: 5 additions & 0 deletions internal/application/ports/provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ type ProvisionOptions struct {
// Used in daemon mode where NodeController handles starting nodes
// via reconciliation instead of the orchestrator starting them directly.
SkipStart bool

// Subnet is the allocated loopback subnet for this devnet (1-254).
// When set, nodes bind to 127.0.{Subnet}.{nodeIndex+1} instead of 0.0.0.0.
// This enables multiple devnets to coexist on the same host without port conflicts.
Subnet uint8
}

// ProvisionResult contains the result of a full provisioning operation.
Expand Down
9 changes: 5 additions & 4 deletions internal/daemon/provisioner/devnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func (p *DevnetProvisioner) Provision(ctx context.Context, devnet *types.Devnet)

// If orchestrator factory is present, execute the full provisioning flow
if p.orchestratorFactory != nil {
result, err := p.provisionWithOrchestrator(ctx, devnet)
result, err := p.provisionWithOrchestrator(ctx, devnet, allocatedSubnet)
if err != nil {
return err
}
Expand All @@ -222,7 +222,7 @@ func (p *DevnetProvisioner) Provision(ctx context.Context, devnet *types.Devnet)

// provisionWithOrchestrator executes the full provisioning flow using an orchestrator.
// Creates an orchestrator for the devnet's network type and returns the provision result.
func (p *DevnetProvisioner) provisionWithOrchestrator(ctx context.Context, devnet *types.Devnet) (*ports.ProvisionResult, error) {
func (p *DevnetProvisioner) provisionWithOrchestrator(ctx context.Context, devnet *types.Devnet, allocatedSubnet uint8) (*ports.ProvisionResult, error) {
// Determine network from devnet spec (Plugin field)
network := devnet.Spec.Plugin
if network == "" {
Expand Down Expand Up @@ -271,7 +271,7 @@ func (p *DevnetProvisioner) provisionWithOrchestrator(ctx context.Context, devne
}

// Convert devnet spec to provisioning options, using plugin defaults when URLs not specified
opts, err := devnetToProvisionOptions(devnet, p.dataDir, networkDefaults)
opts, err := devnetToProvisionOptions(devnet, p.dataDir, networkDefaults, allocatedSubnet)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -547,7 +547,7 @@ func (p *DevnetProvisioner) GetStatus(ctx context.Context, devnet *types.Devnet)
// networkDefaults provides plugin defaults when URLs are not explicitly specified.
//
// Returns SnapshotVersionRequiredError if snapshot mode is detected but no binary version is set.
func devnetToProvisionOptions(devnet *types.Devnet, dataDir string, networkDefaults *NetworkDefaults) (ports.ProvisionOptions, error) {
func devnetToProvisionOptions(devnet *types.Devnet, dataDir string, networkDefaults *NetworkDefaults, allocatedSubnet uint8) (ports.ProvisionOptions, error) {
// Use spec ChainID if set, otherwise generate from devnet name
chainID := devnet.Spec.ChainID
if chainID == "" {
Expand All @@ -561,6 +561,7 @@ func devnetToProvisionOptions(devnet *types.Devnet, dataDir string, networkDefau
NumValidators: devnet.Spec.Validators,
NumFullNodes: devnet.Spec.FullNodes,
DataDir: filepath.Join(dataDir, devnet.Metadata.Name),
Subnet: allocatedSubnet,
}

// Map BinarySource to BinaryPath/BinaryVersion
Expand Down
28 changes: 14 additions & 14 deletions internal/daemon/provisioner/devnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ func TestDevnetToProvisionOptions_BasicConversion(t *testing.T) {
},
}

opts, err := devnetToProvisionOptions(devnet, "/data", nil)
opts, err := devnetToProvisionOptions(devnet, "/data", nil, 0)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand Down Expand Up @@ -690,7 +690,7 @@ func TestDevnetToProvisionOptions_LocalBinarySource(t *testing.T) {
},
}

opts, err := devnetToProvisionOptions(devnet, "/data", nil)
opts, err := devnetToProvisionOptions(devnet, "/data", nil, 0)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand All @@ -717,7 +717,7 @@ func TestDevnetToProvisionOptions_CacheBinarySource(t *testing.T) {
},
}

opts, err := devnetToProvisionOptions(devnet, "/data", nil)
opts, err := devnetToProvisionOptions(devnet, "/data", nil, 0)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand All @@ -741,7 +741,7 @@ func TestDevnetToProvisionOptions_LocalGenesis(t *testing.T) {
},
}

opts, err := devnetToProvisionOptions(devnet, "/data", nil)
opts, err := devnetToProvisionOptions(devnet, "/data", nil, 0)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand Down Expand Up @@ -769,7 +769,7 @@ func TestDevnetToProvisionOptions_SnapshotGenesis(t *testing.T) {
},
}

opts, err := devnetToProvisionOptions(devnet, "/data", nil)
opts, err := devnetToProvisionOptions(devnet, "/data", nil, 0)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand All @@ -793,7 +793,7 @@ func TestDevnetToProvisionOptions_FreshGenesisDefault(t *testing.T) {
},
}

opts, err := devnetToProvisionOptions(devnet, "/data", nil)
opts, err := devnetToProvisionOptions(devnet, "/data", nil, 0)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand All @@ -815,7 +815,7 @@ func TestDevnetToProvisionOptions_RPCGenesisFromSpec(t *testing.T) {
},
}

opts, err := devnetToProvisionOptions(devnet, "/data", nil)
opts, err := devnetToProvisionOptions(devnet, "/data", nil, 0)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand Down Expand Up @@ -843,7 +843,7 @@ func TestDevnetToProvisionOptions_RPCGenesisFromDefaults(t *testing.T) {
RPCURL: "https://default-rpc.example.com",
}

opts, err := devnetToProvisionOptions(devnet, "/data", defaults)
opts, err := devnetToProvisionOptions(devnet, "/data", defaults, 0)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand Down Expand Up @@ -876,7 +876,7 @@ func TestDevnetToProvisionOptions_SnapshotGenesisFromDefaults(t *testing.T) {
RPCURL: "https://default-rpc.example.com",
}

opts, err := devnetToProvisionOptions(devnet, "/data", defaults)
opts, err := devnetToProvisionOptions(devnet, "/data", defaults, 0)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand All @@ -900,7 +900,7 @@ func TestDevnetToProvisionOptions_ChainIDGeneration(t *testing.T) {
},
}

opts, err := devnetToProvisionOptions(devnet, "/data", nil)
opts, err := devnetToProvisionOptions(devnet, "/data", nil, 0)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand All @@ -924,7 +924,7 @@ func TestDevnetToProvisionOptions_SnapshotRequiresVersion(t *testing.T) {
},
}

_, err := devnetToProvisionOptions(devnet, "/data", nil)
_, err := devnetToProvisionOptions(devnet, "/data", nil, 0)
if err == nil {
t.Fatal("Expected SnapshotVersionRequiredError, got nil")
}
Expand Down Expand Up @@ -961,7 +961,7 @@ func TestDevnetToProvisionOptions_SnapshotFromDefaultsRequiresVersion(t *testing
SnapshotURL: "https://default-snapshot.example.com/chain.tar.gz",
}

_, err := devnetToProvisionOptions(devnet, "/data", defaults)
_, err := devnetToProvisionOptions(devnet, "/data", defaults, 0)
if err == nil {
t.Fatal("Expected SnapshotVersionRequiredError, got nil")
}
Expand Down Expand Up @@ -990,7 +990,7 @@ func TestDevnetToProvisionOptions_EmptyTypeWithVersionSet(t *testing.T) {
},
}

opts, err := devnetToProvisionOptions(devnet, "/data", nil)
opts, err := devnetToProvisionOptions(devnet, "/data", nil, 0)
if err != nil {
t.Fatalf("Expected success with empty Type but Version set, got error: %v", err)
}
Expand All @@ -1016,7 +1016,7 @@ func TestDevnetToProvisionOptions_EmptyTypeWithoutVersion(t *testing.T) {
},
}

_, err := devnetToProvisionOptions(devnet, "/data", nil)
_, err := devnetToProvisionOptions(devnet, "/data", nil, 0)
if err == nil {
t.Fatal("Expected SnapshotVersionRequiredError for empty Type and Version")
}
Expand Down
8 changes: 8 additions & 0 deletions internal/daemon/provisioner/orchestrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/altuslabsxyz/devnet-builder/internal/daemon/builder"
"github.com/altuslabsxyz/devnet-builder/internal/daemon/controller"
"github.com/altuslabsxyz/devnet-builder/internal/daemon/runtime"
"github.com/altuslabsxyz/devnet-builder/internal/daemon/subnet"
"github.com/altuslabsxyz/devnet-builder/internal/daemon/types"
"github.com/altuslabsxyz/devnet-builder/internal/infrastructure/nodeconfig"
plugintypes "github.com/altuslabsxyz/devnet-builder/internal/plugin/types"
Expand Down Expand Up @@ -615,6 +616,12 @@ func (o *ProvisioningOrchestrator) initializeNode(ctx context.Context, opts port
return nil, fmt.Errorf("node initialization failed: %w", err)
}

// Compute node address from subnet allocation
var nodeAddress string
if opts.Subnet > 0 {
nodeAddress = subnet.NodeIP(opts.Subnet, index)
}

// Create Node resource
node := &types.Node{
Metadata: types.ResourceMeta{
Expand All @@ -626,6 +633,7 @@ func (o *ProvisioningOrchestrator) initializeNode(ctx context.Context, opts port
Role: role,
BinaryPath: binaryPath,
HomeDir: nodeDir,
Address: nodeAddress,
Desired: types.NodePhaseRunning,
ChainID: opts.ChainID,
Network: opts.Network,
Expand Down
Loading