Skip to content

cardano-testnet: Add --nodes flag for per-node binary configuration#6559

Open
palas wants to merge 13 commits into
masterfrom
testnet-specify-node-bin-per-node
Open

cardano-testnet: Add --nodes flag for per-node binary configuration#6559
palas wants to merge 13 commits into
masterfrom
testnet-specify-node-bin-per-node

Conversation

@palas
Copy link
Copy Markdown
Contributor

@palas palas commented May 6, 2026

Description

Adds a --nodes CLI flag to cardano-testnet that allows specifying the role (SPO or relay) and optionally a custom cardano-node binary for each node in the testnet. This enables running testnets with mixed node versions, which is needed for testing compatibility across node releases.

The new flag coexists with the existing --num-pool-nodes via <|> in the parser, so either can be used:

--nodes spo,spo:node-bin=/path/to/custom-node,relay,relay
--num-pool-nodes 3   # unchanged, still works

At creation time, custom binaries are validated by running --version and the result is recorded in a YAML env file (node-data/nodeN/env). At runtime, the env file is read to resolve the binary, falling back to the default resolution when no env file exists.

Base PR: #6563 (enforces SPOs come first and splits node list into SPO and relay)

Checklist

  • Commit sequence broadly makes sense and commits have useful messages
  • New tests are added if needed and existing tests are updated. These may include:
    • golden tests
    • property tests
    • roundtrip tests
    • integration tests
      See Running tests for more details
  • Any changes are noted in the CHANGELOG.md for affected package
  • The version bounds in .cabal files are updated
  • CI passes. See note on CI. The following CI checks are required:
    • Code is linted with hlint. See .github/workflows/check-hlint.yml to get the hlint version
    • Code is formatted with stylish-haskell. See .github/workflows/stylish-haskell.yml to get the stylish-haskell version
    • Code builds on Linux, MacOS and Windows for ghc-9.6 and ghc-9.12
  • Self-reviewed the diff

@palas palas requested a review from a team as a code owner May 6, 2026 01:59
@palas palas self-assigned this May 6, 2026
@palas palas force-pushed the testnet-specify-node-bin-per-node branch 2 times, most recently from f0bcf8d to 88a49cf Compare May 6, 2026 23:54
@palas palas changed the base branch from master to split-nodes-list-into-spo-and-relay May 6, 2026 23:56
@palas palas changed the title cardano-testnet: Add --nodes flag for per-node binary configuration cardano-testnet: Add --nodes flag for per-node binary configuration May 6, 2026
@palas palas force-pushed the split-nodes-list-into-spo-and-relay branch from 9ca8af4 to 77fa7d8 Compare May 7, 2026 00:06
@palas palas force-pushed the testnet-specify-node-bin-per-node branch from 88a49cf to 450f24b Compare May 7, 2026 00:06
Comment thread cardano-testnet/src/Testnet/Start/Cardano.hs Outdated
Comment thread cardano-testnet/src/Testnet/Runtime.hs Outdated
Comment thread cardano-testnet/src/Testnet/Start/Cardano.hs Outdated
Comment thread cardano-testnet/src/Testnet/Start/Cardano.hs Outdated
Comment thread cardano-testnet/src/Testnet/Start/Cardano.hs Outdated
Comment thread cardano-testnet/src/Testnet/Start/Cardano.hs Outdated
Comment thread cardano-testnet/src/Parsers/Cardano.hs Outdated
Comment thread cardano-testnet/src/Testnet/Start/Types.hs Outdated
Comment thread cardano-testnet/src/Testnet/Start/Cardano.hs
Comment thread cardano-testnet/src/Testnet/Start/Types.hs Outdated
Comment thread cardano-testnet/src/Parsers/Cardano.hs Outdated
Copy link
Copy Markdown
Contributor

@carbolymer carbolymer left a comment

Choose a reason for hiding this comment

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

Exception handling is most important here

@palas palas force-pushed the split-nodes-list-into-spo-and-relay branch from def6fd7 to 393ad15 Compare May 7, 2026 16:19
@palas palas force-pushed the testnet-specify-node-bin-per-node branch from 450f24b to 6530ec1 Compare May 7, 2026 16:19
@palas palas force-pushed the split-nodes-list-into-spo-and-relay branch from 393ad15 to eb5f40e Compare May 7, 2026 16:25
@palas palas force-pushed the testnet-specify-node-bin-per-node branch from 6530ec1 to 8bb24b5 Compare May 7, 2026 16:25
Base automatically changed from split-nodes-list-into-spo-and-relay to master May 7, 2026 17:59
@palas palas force-pushed the testnet-specify-node-bin-per-node branch 2 times, most recently from 744a4ed to 68aa58b Compare May 7, 2026 19:27
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds per-node configuration support to cardano-testnet via a new --nodes CLI flag, allowing each node to be designated as SPO/relay and optionally pinned to a specific cardano-node binary (recorded in the generated env and reused when starting from --node-env).

Changes:

  • Introduces --nodes SPEC[,SPEC...] parsing (with role ordering validation and optional node-bin= path) and updates CLI help golden files.
  • Extends testnet node configuration types to carry an optional per-node binary path, and persists that choice into node-data/nodeN/env.
  • Updates node startup to optionally use a custom binary via a new procCustom helper and a startNode signature change.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Node/Shutdown.hs Updates test to new node options type (NodeWithOptions).
cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/ProposeNewConstitutionSPO.hs Updates test to new node options type (NodeWithOptions).
cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Cli/LeadershipSchedule.hs Updates test node creation + startNode call to pass optional custom binary.
cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Cli/KesPeriodInfo.hs Updates startNode call to pass optional custom binary.
cardano-testnet/test/cardano-testnet-golden/files/golden/help/create-env.cli Updates golden help output to document --nodes.
cardano-testnet/test/cardano-testnet-golden/files/golden/help/cardano.cli Updates golden help output to document --nodes.
cardano-testnet/test/cardano-testnet-golden/files/golden/help.cli Updates top-level golden help usage to show --nodes alternative.
cardano-testnet/src/Testnet/Start/Types.hs Replaces exported node option types with NodeWithOptions/TestnetNodesWithOptions and adds nodeBin.
cardano-testnet/src/Testnet/Start/Cardano.hs Writes/reads per-node env YAML for custom binaries; threads nodeBin into node startup.
cardano-testnet/src/Testnet/Runtime.hs Extends startNode to accept an optional custom node binary.
cardano-testnet/src/Testnet/Process/RunIO.hs Adds procCustom helper for running an explicitly provided binary path.
cardano-testnet/src/Parsers/Run.hs Uses new readNodesWithOptionsFromEnv reader.
cardano-testnet/src/Parsers/Cardano.hs Adds --nodes flag and Parsec-based parseNodeSpecs implementation.
cardano-testnet/src/Cardano/Testnet.hs Updates re-exports to the renamed node options API.
cardano-testnet/changelog.d/20260506_035740_palas_testnet_specify_node_bin_per_node.md Adds changelog entry for --nodes.
cardano-testnet/cardano-testnet.cabal Adds parsec dependency for --nodes parsing.
cardano-node/src/Cardano/Node/Testnet/Paths.hs Adds defaultNodeEnvFile path helper.
cardano-node-chairman/test/Spec/Chairman/Cardano.hs Updates chairman test to use the new default node options value.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread cardano-testnet/src/Testnet/Start/Cardano.hs
Comment thread cardano-testnet/src/Testnet/Start/Cardano.hs
Comment on lines 31 to +36
, UpdateTimestamps(..)
, TestnetOnChainParams(..)
, mainnetParamsRequest
, TestnetNodeOptions(..)
, NodeOptions(..)
, cardanoDefaultTestnetNodeOptions
, TestnetNodesWithOptions(..)
, NodeWithOptions(..)
, cardanoDefaultTestnetNodesWithOptions
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in: 9ff2ba9

Comment thread cardano-testnet/src/Parsers/Cardano.hs
Copy link
Copy Markdown
Contributor

@Jimbo4350 Jimbo4350 left a comment

Choose a reason for hiding this comment

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

LGTM; suggested some minor changes. Will have another look after changes/discussion.

pNodes = OA.option readNodeSpecs
( OA.long "nodes"
<> OA.help "Comma-separated node specifications. SPO nodes must come before relay nodes. \
\Each spec is a role (spo or relay) optionally followed by :node-bin=<path>. \
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.

This needs to exist in documentation somewhere it can't only be in the help text.

specs <- nodeSpec `sepBy1` char ','
let (spos, relays) = span (\(role, _) -> role == Spo) specs
unless (all (\(role, _) -> role == Relay) relays) $
fail "SPO nodes must come before relay nodes. Example: --nodes spo,spo,relay,relay"
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.

Why? Does the order actually matter or is it a matter of the presence of SPOs? Can you also separate the parsing and validation logic here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We have several options, but I thought this was the least problematic one. For example, we could:

  • Allow random orderings: but that would affect a lot of code that assumes SPOs go before relays, this wasn't a problem before because you couldn't specify, also we would need to store a mapping between pools and nodes, which currently are just folders numbered with consecutive numbers starting in 1.
  • Sort them before generating the config: but I think that would be misleading. For example, if someone writes relay,relay,spo,relay, we could silently do node1=spo, node2=relay, node3=relay, node4=relay, but I think that would break the users expectations.
  • Complain when spos are not the first nodes: this forces the user to reorder and avoids the false expectation. Also, it allows us to not have to modify the rest of the code. And it has another very good advantage, we can easily enforce that there is at least one spo by just using a pair (spo=NEL NodeWithOptions, relay=[NodeWithOptions]). If we don't ensure that SPO nodes go first, this becomes tricker.

So I thought the last one (complaining) was the best approach.

About parsing versus validation: I can do that, but I think it is a bad idea, because:

  • As it is, it is impossible to "forget" to validate, and that is very good. In fact, I already forgot validating in one place last week and copilot caught it.
  • The types already enforce what is being validated, so we would need to introduce a new intermediate type that doesn't enforce it for it to be returned by the parser.

Also it goes against the idea of "parse, don't validate".

Comment thread cardano-testnet/src/Parsers/Cardano.hs
Comment thread cardano-testnet/src/Testnet/Start/Cardano.hs
Comment thread cardano-testnet/src/Testnet/Start/Types.hs Outdated
@palas palas force-pushed the testnet-specify-node-bin-per-node branch from 71406e5 to 375d6a9 Compare May 11, 2026 23:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants