Skip to content
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

tmpnet: Add a UUID to temporary networks to support metrics collection #2763

Merged
merged 6 commits into from
Mar 15, 2024
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 .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
Loading