Skip to content

tmpnet: Separate node into orchestration, config and process #2460

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 19, 2023
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
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 |
Copy link
Contributor

Choose a reason for hiding this comment

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

line 107: DefaultRuntime: should be renamed to NodeRuntimeConfig I believe

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

| 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