Skip to content

Commit

Permalink
tmpnet: Add a UUID to temporary networks to support metrics collect…
Browse files Browse the repository at this point in the history
…ion (#2763)
  • Loading branch information
marun authored Mar 15, 2024
1 parent 4e2d005 commit 598018b
Show file tree
Hide file tree
Showing 12 changed files with 73 additions and 93 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ concurrency:

env:
go_version: '~1.21.8'
tmpnet_data_path: ~/.tmpnet/networks/1000
tmpnet_data_path: ~/.tmpnet/networks

jobs:
Unit:
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/ethereum/go-ethereum v1.12.0
github.com/google/btree v1.1.2
github.com/google/renameio/v2 v2.0.0
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/rpc v1.2.0
github.com/gorilla/websocket v1.4.2
Expand Down Expand Up @@ -99,7 +100,6 @@ require (
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/hashicorp/go-bexpr v0.1.10 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
Expand Down
6 changes: 3 additions & 3 deletions tests/e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ $ ./scripts/build_tmpnetctl.sh
# Start a new network
$ ./build/tmpnetctl start-network --avalanchego-path=/path/to/avalanchego
...
Started network 1000 @ /home/me/.tmpnet/networks/1000
Started network /home/me/.tmpnet/networks/20240306-152305.924531 (UUID: abaab590-b375-44f6-9ca5-f8a6dc061725)

Configure tmpnetctl and the test suite to target this network by default
with one of the following statements:
- source /home/me/.tmpnet/networks/1000/network.env
- export TMPNET_NETWORK_DIR=/home/me/.tmpnet/networks/1000
- source /home/me/.tmpnet/networks/20240306-152305.924531/network.env
- export TMPNET_NETWORK_DIR=/home/me/.tmpnet/networks/20240306-152305.924531
- export TMPNET_NETWORK_DIR=/home/me/.tmpnet/networks/latest

# Start a new test run using the existing network
Expand Down
5 changes: 4 additions & 1 deletion tests/e2e/c/dynamic_fees.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ var _ = e2e.DescribeCChain("[Dynamic Fees]", func() {

ginkgo.It("should ensure that the gas price is affected by load", func() {
ginkgo.By("creating a new private network to ensure isolation from other tests")
privateNetwork := e2e.Env.NewPrivateNetwork()
privateNetwork := &tmpnet.Network{
Owner: "avalanchego-e2e-dynamic-fees",
}
e2e.Env.StartPrivateNetwork(privateNetwork)

ginkgo.By("allocating a pre-funded key")
key := privateNetwork.PreFundedKeys[0]
Expand Down
7 changes: 6 additions & 1 deletion tests/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ func init() {

var _ = ginkgo.SynchronizedBeforeSuite(func() []byte {
// Run only once in the first ginkgo process
return e2e.NewTestEnvironment(flagVars, &tmpnet.Network{}).Marshal()
return e2e.NewTestEnvironment(
flagVars,
&tmpnet.Network{
Owner: "avalanchego-e2e",
},
).Marshal()
}, func(envBytes []byte) {
// Run in every ginkgo process

Expand Down
19 changes: 3 additions & 16 deletions tests/fixture/e2e/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ package e2e
import (
"encoding/json"
"math/rand"
"os"
"path/filepath"
"time"

"github.com/stretchr/testify/require"
Expand All @@ -18,7 +16,6 @@ import (
"github.com/ava-labs/avalanchego/tests/fixture"
"github.com/ava-labs/avalanchego/tests/fixture/tmpnet"
"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
"github.com/ava-labs/avalanchego/utils/perms"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"

ginkgo "github.com/onsi/ginkgo/v2"
Expand Down Expand Up @@ -77,7 +74,7 @@ func NewTestEnvironment(flagVars *FlagVars, desiredNetwork *tmpnet.Network) *Tes
}
} else {
network = desiredNetwork
StartNetwork(network, DefaultNetworkDir, flagVars.AvalancheGoExecPath(), flagVars.PluginDir())
StartNetwork(network, flagVars.AvalancheGoExecPath(), flagVars.PluginDir())
}

// A new network will always need subnet creation and an existing
Expand Down Expand Up @@ -158,27 +155,17 @@ func (te *TestEnvironment) NewKeychain(count int) *secp256k1fx.Keychain {
}

// Create a new private network that is not shared with other tests.
func (te *TestEnvironment) NewPrivateNetwork() *tmpnet.Network {
// Load the shared network to retrieve its path and exec path
func (te *TestEnvironment) StartPrivateNetwork(network *tmpnet.Network) {
// Use the same configuration as the shared network
sharedNetwork, err := tmpnet.ReadNetwork(te.NetworkDir)
te.require.NoError(err)

network := &tmpnet.Network{}

// The private networks dir is under the shared network dir to ensure it
// will be included in the artifact uploaded in CI.
privateNetworksDir := filepath.Join(sharedNetwork.Dir, PrivateNetworksDirName)
te.require.NoError(os.MkdirAll(privateNetworksDir, perms.ReadWriteExecute))

pluginDir, err := sharedNetwork.DefaultFlags.GetStringVal(config.PluginDirKey)
te.require.NoError(err)

StartNetwork(
network,
privateNetworksDir,
sharedNetwork.DefaultRuntimeConfig.AvalancheGoPath,
pluginDir,
)

return network
}
4 changes: 2 additions & 2 deletions tests/fixture/e2e/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,15 +216,15 @@ func CheckBootstrapIsPossible(network *tmpnet.Network) {
}

// Start a temporary network with the provided avalanchego binary.
func StartNetwork(network *tmpnet.Network, rootNetworkDir string, avalancheGoExecPath string, pluginDir string) {
func StartNetwork(network *tmpnet.Network, avalancheGoExecPath string, pluginDir string) {
require := require.New(ginkgo.GinkgoT())

require.NoError(
tmpnet.StartNewNetwork(
DefaultContext(),
ginkgo.GinkgoWriter,
network,
rootNetworkDir,
DefaultNetworkDir,
avalancheGoExecPath,
pluginDir,
tmpnet.DefaultNodeCount,
Expand Down
8 changes: 4 additions & 4 deletions tests/fixture/tmpnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ $ ./scripts/build_tmpnetctl.sh
# Start a new network
$ ./build/tmpnetctl start-network --avalanchego-path=/path/to/avalanchego
...
Started network 1000 @ /home/me/.tmpnet/networks/1000
Started network /home/me/.tmpnet/networks/20240306-152305.924531 (UUID: abaab590-b375-44f6-9ca5-f8a6dc061725)

Configure tmpnetctl to target this network by default with one of the following statements:
- source /home/me/.tmpnet/networks/1000/network.env
- export TMPNET_NETWORK_DIR=/home/me/.tmpnet/networks/1000
- source /home/me/.tmpnet/networks/20240306-152305.924531/network.env
- export TMPNET_NETWORK_DIR=/home/me/.tmpnet/networks/20240306-152305.924531
- export TMPNET_NETWORK_DIR=/home/me/.tmpnet/networks/latest

# Stop the network
Expand Down Expand Up @@ -129,7 +129,7 @@ A temporary network relies on configuration written to disk in the following str
HOME
└── .tmpnet // Root path for the temporary network fixture
└── networks // Default parent directory for temporary networks
└── 1000 // The networkID is used to name the network dir and starts at 1000
└── 20240306-152305.924531 // The timestamp of creation is the name of a network's directory
├── NodeID-37E8UK3x2YFsHE3RdALmfWcppcZ1eTuj9 // The ID of a node is the name of its data dir
│ ├── chainData
│ │ └── ...
Expand Down
6 changes: 5 additions & 1 deletion tests/fixture/tmpnet/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func main() {

var (
rootDir string
networkOwner string
avalancheGoPath string
pluginDir string
nodeCount uint8
Expand All @@ -63,7 +64,9 @@ func main() {

// Root dir will be defaulted on start if not provided

network := &tmpnet.Network{}
network := &tmpnet.Network{
Owner: networkOwner,
}

// Extreme upper bound, should never take this long
networkStartTimeout := 2 * time.Minute
Expand Down Expand Up @@ -106,6 +109,7 @@ func main() {
startNetworkCmd.PersistentFlags().StringVar(&avalancheGoPath, "avalanchego-path", os.Getenv(tmpnet.AvalancheGoPathEnvName), "The path to an avalanchego binary")
startNetworkCmd.PersistentFlags().StringVar(&pluginDir, "plugin-dir", os.ExpandEnv("$HOME/.avalanchego/plugins"), "[optional] the dir containing VM plugins")
startNetworkCmd.PersistentFlags().Uint8Var(&nodeCount, "node-count", tmpnet.DefaultNodeCount, "Number of nodes the network should initially consist of")
startNetworkCmd.PersistentFlags().StringVar(&networkOwner, "network-owner", "", "The string identifying the intended owner of the network")
rootCmd.AddCommand(startNetworkCmd)

stopNetworkCmd := &cobra.Command{
Expand Down
97 changes: 36 additions & 61 deletions tests/fixture/tmpnet/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"strconv"
"strings"
"time"

"github.com/google/uuid"

"github.com/ava-labs/avalanchego/config"
"github.com/ava-labs/avalanchego/genesis"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
"github.com/ava-labs/avalanchego/utils/perms"
"github.com/ava-labs/avalanchego/utils/set"
Expand All @@ -40,6 +40,9 @@ const (
// increase the time for a network's nodes to be seen as healthy.
networkHealthCheckInterval = 200 * time.Millisecond

// All temporary networks will use this arbitrary network ID by default.
defaultNetworkID = 88888

// eth address: 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC
HardHatKeyStr = "56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027"
)
Expand All @@ -61,6 +64,16 @@ func init() {

// Collects the configuration for running a temporary avalanchego network
type Network struct {
// Uniquely identifies the temporary network for metrics
// collection. Distinct from avalanchego's concept of network ID
// since the utility of special network ID values (e.g. to trigger
// specific fork behavior in a given network) precludes requiring
// unique network ID values across all temporary networks.
UUID string

// A string identifying the entity that started or maintains this network.
Owner string

// Path where network configuration and data is stored
Dir string

Expand Down Expand Up @@ -150,6 +163,11 @@ func (n *Network) EnsureDefaultConfig(w io.Writer, avalancheGoPath string, plugi
return err
}

// A UUID supports centralized metrics collection
if len(n.UUID) == 0 {
n.UUID = uuid.NewString()
}

// Ensure default flags
if n.DefaultFlags == nil {
n.DefaultFlags = FlagsMap{}
Expand Down Expand Up @@ -206,8 +224,9 @@ func (n *Network) EnsureDefaultConfig(w io.Writer, avalancheGoPath string, plugi
return nil
}

// Creates the network on disk, choosing its network id and generating its genesis in the process.
// Creates the network on disk, generating its genesis and configuring its nodes in the process.
func (n *Network) Create(rootDir string) error {
// Ensure creation of the root dir
if len(rootDir) == 0 {
// Use the default root dir
var err error
Expand All @@ -216,48 +235,34 @@ func (n *Network) Create(rootDir string) error {
return err
}
}

// Ensure creation of the root dir
if err := os.MkdirAll(rootDir, perms.ReadWriteExecute); err != nil {
return fmt.Errorf("failed to create root network dir: %w", err)
}

// Determine the network path and ID
var (
networkDir string
networkID uint32
)
if n.Genesis != nil && n.Genesis.NetworkID > 0 {
// Use the network ID defined in the provided genesis
networkID = n.Genesis.NetworkID
// A time-based name ensures consistent directory ordering
dirName := time.Now().Format("20060102-150405.999999")
if len(n.Owner) > 0 {
// Include the owner to differentiate networks created at similar times
dirName = fmt.Sprintf("%s-%s", dirName, n.Owner)
}
if networkID > 0 {
// Use a directory with a random suffix
var err error
networkDir, err = os.MkdirTemp(rootDir, fmt.Sprintf("%d.", n.Genesis.NetworkID))
if err != nil {
return fmt.Errorf("failed to create network dir: %w", err)
}
} else {
// Find the next available network ID based on the contents of the root dir
var err error
networkID, networkDir, err = findNextNetworkID(rootDir)
if err != nil {
return err
}

// Ensure creation of the network dir
networkDir := filepath.Join(rootDir, dirName)
if err := os.MkdirAll(networkDir, perms.ReadWriteExecute); err != nil {
return fmt.Errorf("failed to create network dir: %w", err)
}
canonicalDir, err := toCanonicalDir(networkDir)
if err != nil {
return err
}
n.Dir = canonicalDir

// Ensure the existence of the plugin directory or nodes won't be able to start.
pluginDir, err := n.DefaultFlags.GetStringVal(config.PluginDirKey)
if err != nil {
return err
}
if len(pluginDir) > 0 {
// Ensure the existence of the plugin directory or nodes won't be able to start.
if err := os.MkdirAll(pluginDir, perms.ReadWriteExecute); err != nil {
return fmt.Errorf("failed to create plugin dir: %w", err)
}
Expand All @@ -275,7 +280,7 @@ func (n *Network) Create(rootDir string) error {
}
keysToFund = append(keysToFund, n.PreFundedKeys...)

genesis, err := NewTestGenesis(networkID, n.Nodes, keysToFund)
genesis, err := NewTestGenesis(defaultNetworkID, n.Nodes, keysToFund)
if err != nil {
return err
}
Expand All @@ -296,7 +301,7 @@ func (n *Network) Create(rootDir string) error {

// Starts all nodes in the network
func (n *Network) Start(ctx context.Context, w io.Writer) error {
if _, err := fmt.Fprintf(w, "Starting network %d @ %s\n", n.Genesis.NetworkID, n.Dir); err != nil {
if _, err := fmt.Fprintf(w, "Starting network %s (UUID: %s)\n", n.Dir, n.UUID); err != nil {
return err
}

Expand All @@ -313,7 +318,7 @@ func (n *Network) Start(ctx context.Context, w io.Writer) error {
if err := n.WaitForHealthy(ctx, w); err != nil {
return err
}
if _, err := fmt.Fprintf(w, "\nStarted network %d @ %s\n", n.Genesis.NetworkID, n.Dir); err != nil {
if _, err := fmt.Fprintf(w, "\nStarted network %s (UUID: %s)\n", n.Dir, n.UUID); err != nil {
return err
}

Expand Down Expand Up @@ -676,33 +681,3 @@ func getDefaultRootDir() (string, error) {
}
return filepath.Join(homeDir, ".tmpnet", "networks"), nil
}

// Finds the next available network ID by attempting to create a
// directory numbered from 1000 until creation succeeds. Returns the
// network id and the full path of the created directory.
func findNextNetworkID(rootDir string) (uint32, string, error) {
var (
networkID uint32 = 1000
dirPath string
)
for {
_, reserved := constants.NetworkIDToNetworkName[networkID]
if reserved {
networkID++
continue
}

dirPath = filepath.Join(rootDir, strconv.FormatUint(uint64(networkID), 10))
err := os.Mkdir(dirPath, perms.ReadWriteExecute)
if err == nil {
return networkID, dirPath, nil
}

if !errors.Is(err, fs.ErrExist) {
return 0, "", fmt.Errorf("failed to create network directory: %w", err)
}

// Directory already exists, keep iterating
networkID++
}
}
Loading

0 comments on commit 598018b

Please sign in to comment.