Skip to content

Commit a6d0977

Browse files
committed
[tmpnet] Delegate writing of the flag file to the runtime
This is intended to enable the process and kube runtimes to vary how they compose the flags that configure a given node.
1 parent 8c08786 commit a6d0977

File tree

8 files changed

+150
-132
lines changed

8 files changed

+150
-132
lines changed

tests/antithesis/compose.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ func newComposeProject(network *tmpnet.Network, nodeImageName string, workloadIm
161161
}
162162

163163
// Apply configuration appropriate to a test network
164-
for k, v := range tmpnet.DefaultTestFlags() {
164+
for k, v := range tmpnet.DefaultTmpnetFlags() {
165165
env[k] = v
166166
}
167167

tests/e2e/e2e_test.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,22 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte {
6161
require.NoError(tc, err)
6262

6363
upgradeBase64 := base64.StdEncoding.EncodeToString(upgradeJSON)
64+
65+
defaultFlags := tmpnet.FlagsMap{
66+
config.UpgradeFileContentKey: upgradeBase64,
67+
// Ensure a min stake duration compatible with testing staking logic
68+
config.MinStakeDurationKey: "1s",
69+
}
70+
defaultFlags.SetDefaults(tmpnet.DefaultE2EFlags())
71+
6472
return e2e.NewTestEnvironment(
6573
tc,
6674
flagVars,
6775
&tmpnet.Network{
68-
Owner: flagVars.NetworkOwner(),
69-
DefaultFlags: tmpnet.FlagsMap{
70-
config.UpgradeFileContentKey: upgradeBase64,
71-
// Ensure a min stake duration compatible with testing staking logic
72-
config.MinStakeDurationKey: tmpnet.DefaultMinStakeDuration,
73-
},
74-
Nodes: nodes,
75-
Subnets: subnets,
76+
Owner: flagVars.NetworkOwner(),
77+
DefaultFlags: defaultFlags,
78+
Nodes: nodes,
79+
Subnets: subnets,
7680
},
7781
).Marshal()
7882
}, func(envBytes []byte) {

tests/fixture/tmpnet/defaults.go

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ const (
3535
defaultConfigFilename = "config.json"
3636
)
3737

38-
// Flags appropriate for networks used for all types of testing.
39-
func DefaultTestFlags() FlagsMap {
38+
// Flags suggested for temporary networks. Applied by default.
39+
func DefaultTmpnetFlags() FlagsMap {
4040
return FlagsMap{
4141
config.NetworkPeerListPullGossipFreqKey: "250ms",
4242
config.NetworkMaxReconnectDelayKey: "1s",
@@ -46,26 +46,14 @@ func DefaultTestFlags() FlagsMap {
4646
}
4747
}
4848

49-
// Flags appropriate for tmpnet networks.
50-
func DefaultTmpnetFlags() FlagsMap {
51-
// Supply only non-default configuration to ensure that default values will be used.
52-
flags := FlagsMap{
53-
// Default to dynamic port allocation
54-
config.HTTPPortKey: "0",
55-
config.StakingPortKey: "0",
56-
// Specific to tmpnet deployment
57-
config.PublicIPKey: "127.0.0.1",
58-
config.HTTPHostKey: "127.0.0.1",
59-
config.StakingHostKey: "127.0.0.1",
60-
config.LogDisplayLevelKey: logging.Off.String(), // Display logging not needed since nodes run headless
61-
config.LogLevelKey: logging.Debug.String(),
62-
// Specific to e2e testing
49+
// Flags suggested for e2e testing
50+
func DefaultE2EFlags() FlagsMap {
51+
return FlagsMap{
6352
config.ProposerVMUseCurrentHeightKey: "true",
6453
// Reducing this from the 1s default speeds up tx acceptance
6554
config.ProposerVMMinBlockDelayKey: "0s",
55+
config.LogLevelKey: logging.Debug.String(),
6656
}
67-
flags.SetDefaults(DefaultTestFlags())
68-
return flags
6957
}
7058

7159
// A set of chain configurations appropriate for testing.

tests/fixture/tmpnet/network.go

Lines changed: 12 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"encoding/json"
1212
"errors"
1313
"fmt"
14-
"maps"
1514
"net/netip"
1615
"os"
1716
"os/exec"
@@ -65,7 +64,8 @@ var (
6564
// TODO(marun) Remove when subnet-evm configures the genesis with this key.
6665
HardhatKey *secp256k1.PrivateKey
6766

68-
errInsufficientNodes = errors.New("at least one node is required")
67+
errInsufficientNodes = errors.New("at least one node is required")
68+
errMissingRuntimeConfig = errors.New("DefaultRuntimeConfig must not be empty")
6969
)
7070

7171
func init() {
@@ -253,6 +253,11 @@ func (n *Network) EnsureDefaultConfig(log logging.Logger) error {
253253
}
254254
}
255255

256+
emptyRuntime := NodeRuntimeConfig{}
257+
if n.DefaultRuntimeConfig == emptyRuntime {
258+
return errMissingRuntimeConfig
259+
}
260+
256261
return nil
257262
}
258263

@@ -471,10 +476,6 @@ func (n *Network) StartNode(ctx context.Context, node *Node) error {
471476
return err
472477
}
473478

474-
if err := n.writeNodeFlags(node); err != nil {
475-
return fmt.Errorf("writing node flags: %w", err)
476-
}
477-
478479
if err := node.Start(ctx); err != nil {
479480
// Attempt to stop an unhealthy node to provide some assurance to the caller
480481
// that an error condition will not result in a lingering process.
@@ -762,10 +763,11 @@ func (n *Network) GetNodeURIs() []NodeURI {
762763
return GetNodeURIs(n.Nodes)
763764
}
764765

765-
// Retrieves bootstrap IPs and IDs for all nodes except the skipped one (this supports
766-
// collecting the bootstrap details for restarting a node).
766+
// Retrieves bootstrap IPs and IDs for all non-ephemeral nodes except the skipped one
767+
// (this supports collecting the bootstrap details for restarting a node).
768+
//
767769
// For consumption outside of avalanchego. Needs to be kept exported.
768-
func (n *Network) GetBootstrapIPsAndIDs(skippedNode *Node) ([]string, []string, error) {
770+
func (n *Network) GetBootstrapIPsAndIDs(skippedNode *Node) ([]string, []string) {
769771
bootstrapIPs := []string{}
770772
bootstrapIDs := []string{}
771773
for _, node := range n.Nodes {
@@ -786,7 +788,7 @@ func (n *Network) GetBootstrapIPsAndIDs(skippedNode *Node) ([]string, []string,
786788
bootstrapIDs = append(bootstrapIDs, node.NodeID.String())
787789
}
788790

789-
return bootstrapIPs, bootstrapIDs, nil
791+
return bootstrapIPs, bootstrapIDs
790792
}
791793

792794
// GetNetworkID returns the effective ID of the network. If the network
@@ -879,77 +881,6 @@ func (n *Network) GetChainConfigContent() (string, error) {
879881
return base64.StdEncoding.EncodeToString(marshaledConfigs), nil
880882
}
881883

882-
// writeNodeFlags determines the set of flags that should be used to
883-
// start the given node and writes them to a file in the node path.
884-
func (n *Network) writeNodeFlags(node *Node) error {
885-
flags := maps.Clone(node.Flags)
886-
887-
// Convert the network id to a string to ensure consistency in JSON round-tripping.
888-
flags.SetDefault(config.NetworkNameKey, strconv.FormatUint(uint64(n.GetNetworkID()), 10))
889-
890-
// Set the bootstrap configuration
891-
bootstrapIPs, bootstrapIDs, err := n.GetBootstrapIPsAndIDs(node)
892-
if err != nil {
893-
return fmt.Errorf("failed to determine bootstrap configuration: %w", err)
894-
}
895-
flags.SetDefault(config.BootstrapIDsKey, strings.Join(bootstrapIDs, ","))
896-
flags.SetDefault(config.BootstrapIPsKey, strings.Join(bootstrapIPs, ","))
897-
898-
// TODO(marun) Maybe avoid computing content flags for each node start?
899-
900-
if n.Genesis != nil {
901-
genesisFileContent, err := n.GetGenesisFileContent()
902-
if err != nil {
903-
return fmt.Errorf("failed to get genesis file content: %w", err)
904-
}
905-
flags.SetDefault(config.GenesisFileContentKey, genesisFileContent)
906-
907-
isSingleNodeNetwork := (len(n.Nodes) == 1 && len(n.Genesis.InitialStakers) == 1)
908-
if isSingleNodeNetwork {
909-
n.log.Info("defaulting to sybil protection disabled to enable a single-node network to start")
910-
flags.SetDefault(config.SybilProtectionEnabledKey, "false")
911-
}
912-
}
913-
914-
subnetConfigContent, err := n.GetSubnetConfigContent()
915-
if err != nil {
916-
return fmt.Errorf("failed to get subnet config content: %w", err)
917-
}
918-
if len(subnetConfigContent) > 0 {
919-
flags.SetDefault(config.SubnetConfigContentKey, subnetConfigContent)
920-
}
921-
922-
chainConfigContent, err := n.GetChainConfigContent()
923-
if err != nil {
924-
return fmt.Errorf("failed to get chain config content: %w", err)
925-
}
926-
if len(chainConfigContent) > 0 {
927-
flags.SetDefault(config.ChainConfigContentKey, chainConfigContent)
928-
}
929-
930-
// Only configure the plugin dir with a non-empty value to ensure the use of
931-
// the default value (`[datadir]/plugins`) when no plugin dir is configured.
932-
processConfig := node.getRuntimeConfig().Process
933-
if processConfig != nil {
934-
if len(processConfig.PluginDir) > 0 {
935-
// Ensure the plugin directory exists or the node will fail to start
936-
if err := os.MkdirAll(processConfig.PluginDir, perms.ReadWriteExecute); err != nil {
937-
return fmt.Errorf("failed to create plugin dir: %w", err)
938-
}
939-
flags.SetDefault(config.PluginDirKey, processConfig.PluginDir)
940-
}
941-
942-
flags.SetDefault(config.DataDirKey, node.DataDir)
943-
}
944-
945-
// Set the network and tmpnet defaults last to ensure they can be overridden
946-
flags.SetDefaults(n.DefaultFlags)
947-
flags.SetDefaults(DefaultTmpnetFlags())
948-
949-
// Write the flags to disk
950-
return node.writeFlags(flags)
951-
}
952-
953884
// Waits until the provided nodes are healthy.
954885
func waitForHealthy(ctx context.Context, log logging.Logger, nodes []*Node) error {
955886
ticker := time.NewTicker(networkHealthCheckInterval)

tests/fixture/tmpnet/network_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ func TestNetworkSerialization(t *testing.T) {
2020
ctx := context.Background()
2121

2222
network := NewDefaultNetwork("testnet")
23+
// Runtime configuration is required
24+
network.DefaultRuntimeConfig.Process = &ProcessRuntimeConfig{}
2325
// Validate round-tripping of primary subnet configuration
2426
network.PrimarySubnetConfig = ConfigMap{
2527
"validatorOnly": true,

tests/fixture/tmpnet/node.go

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import (
99
"errors"
1010
"fmt"
1111
"io"
12+
"maps"
1213
"net"
1314
"net/http"
1415
"net/netip"
16+
"strconv"
1517
"strings"
1618
"time"
1719

@@ -53,12 +55,6 @@ type NodeRuntimeConfig struct {
5355
Process *ProcessRuntimeConfig `json:"process,omitempty"`
5456
}
5557

56-
type ProcessRuntimeConfig struct {
57-
AvalancheGoPath string `json:"avalancheGoPath,omitempty"`
58-
PluginDir string `json:"pluginDir,omitempty"`
59-
ReuseDynamicPorts bool `json:"reuseDynamicPorts,omitempty"`
60-
}
61-
6258
// Node supports configuring and running a node participating in a temporary network.
6359
type Node struct {
6460
// Set by EnsureNodeID which is also called when the node is read.
@@ -323,6 +319,59 @@ func (n *Node) GetUniqueID() string {
323319
return n.network.UUID + "-" + strings.ToLower(nodeIDString[startIndex:endIndex])
324320
}
325321

322+
// composeFlags determines the set of flags that should be used to
323+
// start the node.
324+
func (n *Node) composeFlags() (FlagsMap, error) {
325+
flags := maps.Clone(n.Flags)
326+
327+
// Apply the network defaults first so that they are not overridden
328+
flags.SetDefaults(n.network.DefaultFlags)
329+
330+
flags.SetDefaults(DefaultTmpnetFlags())
331+
332+
// Convert the network id to a string to ensure consistency in JSON round-tripping.
333+
flags.SetDefault(config.NetworkNameKey, strconv.FormatUint(uint64(n.network.GetNetworkID()), 10))
334+
335+
// Set the bootstrap configuration
336+
bootstrapIPs, bootstrapIDs := n.network.GetBootstrapIPsAndIDs(n)
337+
flags.SetDefault(config.BootstrapIDsKey, strings.Join(bootstrapIDs, ","))
338+
flags.SetDefault(config.BootstrapIPsKey, strings.Join(bootstrapIPs, ","))
339+
340+
// TODO(marun) Maybe avoid computing content flags for each node start?
341+
342+
if n.network.Genesis != nil {
343+
genesisFileContent, err := n.network.GetGenesisFileContent()
344+
if err != nil {
345+
return nil, fmt.Errorf("failed to get genesis file content: %w", err)
346+
}
347+
flags.SetDefault(config.GenesisFileContentKey, genesisFileContent)
348+
349+
isSingleNodeNetwork := (len(n.network.Nodes) == 1 && len(n.network.Genesis.InitialStakers) == 1)
350+
if isSingleNodeNetwork {
351+
n.network.log.Info("defaulting to sybil protection disabled to enable a single-node network to start")
352+
flags.SetDefault(config.SybilProtectionEnabledKey, "false")
353+
}
354+
}
355+
356+
subnetConfigContent, err := n.network.GetSubnetConfigContent()
357+
if err != nil {
358+
return nil, fmt.Errorf("failed to get subnet config content: %w", err)
359+
}
360+
if len(subnetConfigContent) > 0 {
361+
flags.SetDefault(config.SubnetConfigContentKey, subnetConfigContent)
362+
}
363+
364+
chainConfigContent, err := n.network.GetChainConfigContent()
365+
if err != nil {
366+
return nil, fmt.Errorf("failed to get chain config content: %w", err)
367+
}
368+
if len(chainConfigContent) > 0 {
369+
flags.SetDefault(config.ChainConfigContentKey, chainConfigContent)
370+
}
371+
372+
return flags, nil
373+
}
374+
326375
// Saves the currently allocated API port to the node's configuration
327376
// for use across restarts.
328377
func (n *Node) SaveAPIPort() error {

tests/fixture/tmpnet/node_config.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,6 @@ func (n *Node) GetFlagsPath() string {
2323
return filepath.Join(n.DataDir, "flags.json")
2424
}
2525

26-
func (n *Node) writeFlags(flags FlagsMap) error {
27-
bytes, err := DefaultJSONMarshal(flags)
28-
if err != nil {
29-
return fmt.Errorf("failed to marshal node flags: %w", err)
30-
}
31-
if err := os.WriteFile(n.GetFlagsPath(), bytes, perms.ReadWrite); err != nil {
32-
return fmt.Errorf("failed to write node flags: %w", err)
33-
}
34-
return nil
35-
}
36-
3726
func (n *Node) getConfigPath() string {
3827
return filepath.Join(n.DataDir, defaultConfigFilename)
3928
}

0 commit comments

Comments
 (0)