Skip to content

Commit

Permalink
tmpnet: Separate node into orchestration, config and process (#2460)
Browse files Browse the repository at this point in the history
  • Loading branch information
marun authored Dec 19, 2023
1 parent 618f02c commit fa21d78
Show file tree
Hide file tree
Showing 15 changed files with 554 additions and 357 deletions.
2 changes: 1 addition & 1 deletion tests/e2e/faultinjection/duplicate_node_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ var _ = ginkgo.Describe("Duplicate node handling", func() {
require.ErrorIs(err, context.DeadlineExceeded)

ginkgo.By("stopping the first new node")
require.NoError(node1.Stop())
require.NoError(node1.Stop(e2e.DefaultContext()))

ginkgo.By("checking that the second new node becomes healthy within timeout")
e2e.WaitForHealthy(node2)
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/p/interchain_workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ var _ = e2e.DescribePChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainL
require.Positive(balance.Cmp(big.NewInt(0)))

ginkgo.By("stopping validator node to free up resources for a bootstrap check")
require.NoError(node.Stop())
require.NoError(node.Stop(e2e.DefaultContext()))

e2e.CheckBootstrapIsPossible(network)
})
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/p/staking_rewards.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ var _ = ginkgo.Describe("[Staking Rewards]", func() {
})

ginkgo.By("stopping beta node to prevent it and its delegator from receiving a validation reward")
require.NoError(betaNode.Stop())
require.NoError(betaNode.Stop(e2e.DefaultContext()))

ginkgo.By("retrieving staking periods from the chain")
data, err := pvmClient.GetCurrentValidators(e2e.DefaultContext(), constants.PlatformChainID, []ids.NodeID{alphaNodeID})
Expand Down Expand Up @@ -302,7 +302,7 @@ var _ = ginkgo.Describe("[Staking Rewards]", func() {
}

ginkgo.By("stopping alpha to free up resources for a bootstrap check")
require.NoError(alphaNode.Stop())
require.NoError(alphaNode.Stop(e2e.DefaultContext()))

e2e.CheckBootstrapIsPossible(network)
})
Expand Down
2 changes: 1 addition & 1 deletion tests/fixture/e2e/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,5 +132,5 @@ func (te *TestEnvironment) NewPrivateNetwork() *tmpnet.Network {
privateNetworksDir := filepath.Join(sharedNetwork.Dir, PrivateNetworksDirName)
te.require.NoError(os.MkdirAll(privateNetworksDir, perms.ReadWriteExecute))

return StartNetwork(sharedNetwork.ExecPath, privateNetworksDir)
return StartNetwork(sharedNetwork.AvalancheGoPath, privateNetworksDir)
}
38 changes: 13 additions & 25 deletions tests/fixture/e2e/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,22 +118,21 @@ func Eventually(condition func() bool, waitFor time.Duration, tick time.Duration
}
}

// Add an ephemeral node that is only intended to be used by a single test. Its ID and
// URI are not intended to be returned from the Network instance to minimize
// accessibility from other tests.
// Adds an ephemeral node intended to be used by a single test.
func AddEphemeralNode(network *tmpnet.Network, flags tmpnet.FlagsMap) *tmpnet.Node {
require := require.New(ginkgo.GinkgoT())

node, err := network.AddEphemeralNode(ginkgo.GinkgoWriter, flags)
ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancel()
node, err := network.AddEphemeralNode(ctx, ginkgo.GinkgoWriter, flags)
require.NoError(err)

// Ensure node is stopped on teardown. It's configuration is not removed to enable
// collection in CI to aid in troubleshooting failures.
ginkgo.DeferCleanup(func() {
tests.Outf("Shutting down ephemeral node %s\n", node.NodeID)
require.NoError(node.Stop())
tests.Outf("shutting down ephemeral node %q\n", node.NodeID)
ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancel()
require.NoError(node.Stop(ctx))
})

return node
}

Expand Down Expand Up @@ -191,26 +190,13 @@ func WithSuggestedGasPrice(ethClient ethclient.Client) common.Option {

// Verify that a new node can bootstrap into the network.
func CheckBootstrapIsPossible(network *tmpnet.Network) {
require := require.New(ginkgo.GinkgoT())

if len(os.Getenv(SkipBootstrapChecksEnvName)) > 0 {
tests.Outf("{{yellow}}Skipping bootstrap check due to the %s env var being set", SkipBootstrapChecksEnvName)
return
}
ginkgo.By("checking if bootstrap is possible with the current network state")

// Call network.AddEphemeralNode instead of AddEphemeralNode to support
// checking for bootstrap implicitly on teardown via a function registered
// with ginkgo.DeferCleanup. It's not possible to call DeferCleanup from
// within a function called by DeferCleanup.
node, err := network.AddEphemeralNode(ginkgo.GinkgoWriter, tmpnet.FlagsMap{})
require.NoError(err)

defer func() {
tests.Outf("Shutting down ephemeral node %s\n", node.NodeID)
require.NoError(node.Stop())
}()

node := AddEphemeralNode(network, tmpnet.FlagsMap{})
WaitForHealthy(node)
}

Expand All @@ -224,7 +210,7 @@ func StartNetwork(avalancheGoExecPath string, networkDir string) *tmpnet.Network
networkDir,
&tmpnet.Network{
NodeRuntimeConfig: tmpnet.NodeRuntimeConfig{
ExecPath: avalancheGoExecPath,
AvalancheGoPath: avalancheGoExecPath,
},
},
tmpnet.DefaultNodeCount,
Expand All @@ -233,7 +219,9 @@ func StartNetwork(avalancheGoExecPath string, networkDir string) *tmpnet.Network
require.NoError(err)
ginkgo.DeferCleanup(func() {
tests.Outf("Shutting down network\n")
require.NoError(network.Stop())
ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancel()
require.NoError(network.Stop(ctx))
})

tests.Outf("{{green}}Successfully started network{{/}}\n")
Expand Down
35 changes: 24 additions & 11 deletions tests/fixture/tmpnet/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# tmpnet (temporary network fixture)
# tmpnet - temporary network orchestration

This package implements a simple orchestrator for the avalanchego
nodes of a temporary network. Configuration is stored on disk, and
Expand Down Expand Up @@ -31,6 +31,8 @@ the following non-test files:
| genesis.go | | Creates test genesis |
| network.go | Network | Orchestrates and configures temporary networks |
| node.go | Node | Orchestrates and configures nodes |
| node_config.go | Node | Reads and writes node configuration |
| node_process.go | NodeProcess | Orchestrates node processes |
| utils.go | | Defines shared utility functions |

## Usage
Expand Down Expand Up @@ -76,7 +78,7 @@ network, _ := tmpnet.StartNetwork(
ginkgo.GinkgoWriter, // Writer to report progress of network start
"", // Use default root dir (~/.tmpnet)
&tmpnet.Network{
DefaultRuntime: tmpnet.NodeRuntimeConfig{
NodeRuntimeConfig: tmpnet.NodeRuntimeConfig{
ExecPath: "/path/to/avalanchego", // Defining the avalanchego exec path is required
},
},
Expand All @@ -102,7 +104,7 @@ network, _ := tmpnet.StartNetwork(
ginkgo.GinkgoWriter,
"",
&tmpnet.Network{
DefaultRuntime: tmpnet.NodeRuntimeConfig{
NodeRuntimeConfig: tmpnet.NodeRuntimeConfig{
ExecPath: "/path/to/avalanchego",
},
Nodes: []*Node{
Expand Down Expand Up @@ -147,9 +149,10 @@ HOME
├── NodeID-37E8UK3x2YFsHE3RdALmfWcppcZ1eTuj9 // The ID of a node is the name of its data dir
│ ├── chainData
│ │ └── ...
│ ├── config.json // Node flags
│ ├── config.json // Node runtime configuration
│ ├── db
│ │ └── ...
│ ├── flags.json // Node flags
│ ├── logs
│ │ └── ...
│ ├── plugins
Expand All @@ -160,11 +163,7 @@ HOME
│ └── config.json // C-Chain config for all nodes
├── defaults.json // Default flags and configuration for network
├── genesis.json // Genesis for all nodes
├── network.env // Sets network dir env to simplify use of network
└── ephemeral // Parent directory for ephemeral nodes (e.g. created by tests)
└─ NodeID-FdxnAvr4jK9XXAwsYZPgWAHW2QnwSZ // Data dir for an ephemeral node
└── ...
└── network.env // Sets network dir env var to simplify network usage
```

### Default flags and configuration
Expand Down Expand Up @@ -203,19 +202,33 @@ this file (i.e. `source network.env`) in a shell will configure ginkgo
e2e and the `tmpnetctl` cli to target the network path specified in
the env var.

Set `TMPNET_ROOT_DIR` to specify the root directory in which to create
the configuration directory of new networks
(e.g. `$TMPNET_ROOT_DIR/[network-dir]`). The default root directory is
`~/.tmpdir/networks`. Configuring the root directory is only relevant
when creating new networks as the path of existing networks will
already have been set.

### Node configuration

The data dir for a node is set by default to
`[network-path]/[node-id]`. A node can be configured to use a
non-default path by explicitly setting the `--data-dir`
flag.

#### Runtime config

The details required to configure a node's execution are written to
`[network-path]/[node-id]/config.json`. This file contains the
runtime-specific details like the path of the avalanchego binary to
start the node with.

#### Flags

All flags used to configure a node are written to
`[network-path]/[node-id]/config.json` so that a node can be
`[network-path]/[node-id]/flags.json` so that a node can be
configured with only a single argument:
`--config-file=/path/to/config.json`. This simplifies node launch and
`--config-file=/path/to/flags.json`. This simplifies node launch and
ensures all parameters used to launch a node can be modified by
editing the config file.

Expand Down
8 changes: 5 additions & 3 deletions tests/fixture/tmpnet/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ func main() {

network := &tmpnet.Network{
NodeRuntimeConfig: tmpnet.NodeRuntimeConfig{
ExecPath: execPath,
AvalancheGoPath: execPath,
},
}
ctx, cancel := context.WithTimeout(context.Background(), tmpnet.DefaultNetworkStartTimeout)
ctx, cancel := context.WithTimeout(context.Background(), tmpnet.DefaultNetworkTimeout)
defer cancel()
network, err := tmpnet.StartNetwork(ctx, os.Stdout, rootDir, network, int(nodeCount), int(preFundedKeyCount))
if err != nil {
Expand Down Expand Up @@ -105,7 +105,9 @@ func main() {
if len(networkDir) == 0 {
return errNetworkDirRequired
}
if err := tmpnet.StopNetwork(networkDir); err != nil {
ctx, cancel := context.WithTimeout(context.Background(), tmpnet.DefaultNetworkTimeout)
defer cancel()
if err := tmpnet.StopNetwork(ctx, networkDir); err != nil {
return err
}
fmt.Fprintf(os.Stdout, "Stopped network configured at: %s\n", networkDir)
Expand Down
11 changes: 5 additions & 6 deletions tests/fixture/tmpnet/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,10 @@ import (
const (
// Constants defining the names of shell variables whose value can
// configure temporary network orchestration.
AvalancheGoPathEnvName = "AVALANCHEGO_PATH"
NetworkDirEnvName = "TMPNET_NETWORK_DIR"
RootDirEnvName = "TMPNET_ROOT_DIR"
NetworkDirEnvName = "TMPNET_NETWORK_DIR"
RootDirEnvName = "TMPNET_ROOT_DIR"

DefaultNetworkStartTimeout = 2 * time.Minute
DefaultNodeInitTimeout = 10 * time.Second
DefaultNodeStopTimeout = 5 * time.Second
DefaultNetworkTimeout = 2 * time.Minute

// Minimum required to ensure connectivity-based health checks will pass
DefaultNodeCount = 2
Expand All @@ -28,6 +25,8 @@ const (

// A short minimum stake duration enables testing of staking logic.
DefaultMinStakeDuration = time.Second

defaultConfigFilename = "config.json"
)

// A set of flags appropriate for testing.
Expand Down
Loading

0 comments on commit fa21d78

Please sign in to comment.