Files
netrunner/README.md
T

396 lines
13 KiB
Markdown
Raw Normal View History

2021-11-05 11:07:05 -04:00
# Avalanche Network Runner
2021-11-04 20:23:53 -03:00
2021-12-20 16:16:13 -05:00
## Note
This tool is under heavy development and the documentation/code snippers below may vary slightly from the actual code in the repository.
Updates to the documentation may happen some time after an update to the codebase.
Nonetheless, this README should provide valuable information about using this tool.
2021-11-04 20:23:53 -03:00
## Overview
2021-11-05 11:07:05 -04:00
This is a tool to run and interact with an Avalanche network.
The nodes that compose the network can run either locally or in a Kubernetes cluster.
This tool may be especially useful for development and testing.
2021-11-04 20:23:53 -03:00
2021-11-05 11:07:05 -04:00
The network you run can be:
2021-11-04 20:23:53 -03:00
2021-11-05 11:07:05 -04:00
1. Locally: each node runs in a process on your computer.
2. In Kubernetes: each node runs in a Kubernetes pod. The Kubernetes pods may be on your computer or remote.
2021-11-04 20:23:53 -03:00
2021-11-05 11:07:05 -04:00
## Configuration
2021-11-04 20:23:53 -03:00
2021-11-05 11:07:05 -04:00
When the user creates a network, they specify the configurations of the nodes that are in the network upon creation.
2021-11-05 11:19:08 -03:00
2021-11-05 11:07:05 -04:00
A node config is defined by this struct:
2021-11-04 20:23:53 -03:00
2021-11-05 11:07:05 -04:00
```go
type Config struct {
// Configuration specific to a particular implementation of a node.
ImplSpecificConfig interface{}
// A node's name must be unique from all other nodes
// in a network. If Name is the empty string, a
// unique name is assigned on node creation.
Name string
// True if other nodes should use this node
// as a bootstrap beacon.
IsBeacon bool
// Must not be nil
StakingKey []byte
// Must not be nil
StakingCert []byte
// May be nil.
ConfigFile []byte
// May be nil.
CChainConfigFile []byte
2021-11-05 11:07:05 -04:00
}
2021-11-04 20:23:53 -03:00
```
2021-11-05 11:07:05 -04:00
As you can see, some fields of the config must be set, while others will be auto-generated if not provided.
2021-11-23 11:21:37 -05:00
Bootstrap IPs/ IDs will be overwritten even if provided.
2021-11-05 11:19:08 -03:00
2021-11-04 20:23:53 -03:00
2021-11-05 11:07:05 -04:00
A node's configuration may include fields that are specific to the type of network runner being used (see `ImplSpecificConfig` in the struct above.)
For example, a node running in a Kubernetes cluster has a config field that specifies the Docker image that the node runs,
whereas a node running locally has a config field that specifies the path of the binary that the node runs.
2021-11-04 20:23:53 -03:00
## Genesis Generation
2021-11-22 18:53:08 -05:00
You can create a custom AvalancheGo genesis with function `network.NewAvalancheGoGenesis`:
```go
// Return a genesis JSON where:
// The nodes in [genesisVdrs] are validators.
// The C-Chain and X-Chain balances are given by
// [cChainBalances] and [xChainBalances].
// Note that many of the genesis fields (i.e. reward addresses)
// are randomly generated or hard-coded.
func NewAvalancheGoGenesis(
log logging.Logger,
networkID uint32,
xChainBalances []AddrAndBalance,
cChainBalances []AddrAndBalance,
genesisVdrs []ids.ShortID,
) ([]byte, error)
```
Later on the genesis contents can be used in network creation.
2021-11-05 11:07:05 -04:00
## Network Creation
2021-11-04 20:23:53 -03:00
2021-11-19 15:55:01 -03:00
Each network runner implementation (local/Kubernetes) has a function `NewNetwork` that returns a new network,
parameterized on `network.Config`:
2021-11-05 11:19:08 -03:00
2021-11-05 11:07:05 -04:00
```go
type Config struct {
// Configuration specific to a particular implementation of a network.
ImplSpecificConfig interface{}
// Must not be nil
Genesis []byte
// May have length 0
// (i.e. network may have no nodes on creation.)
NodeConfigs []node.Config
// Log level for the whole network
LogLevel string
// Name for the network
Name string
// How many nodes in the network.
// TODO move to k8s package?
NodeCount int
2021-11-05 11:07:05 -04:00
}
2021-11-04 20:23:53 -03:00
```
2021-11-05 11:07:05 -04:00
The function that returns a new network may have additional configuration fields.
2021-11-04 20:23:53 -03:00
2021-11-19 15:55:01 -03:00
## Default Network Creation
2021-11-19 15:38:33 -05:00
The local network runner implementation includes a helper function `NewDefaultNetwork`, which returns a network using a pre-defined configuration.
This allows users to create a new network without needing to define any configurations.
2021-11-19 15:55:01 -03:00
2021-11-19 19:37:55 -03:00
```go
2021-11-19 15:38:33 -05:00
// NewDefaultNetwork returns a new network using a pre-defined
// network configuration.
// The following addresses are pre-funded:
// X-Chain Address 1: X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p
// X-Chain Address 1 Key: PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN
// X-Chain Address 2: X-custom16045mxr3s2cjycqe2xfluk304xv3ezhkhsvkpr
// X-Chain Address 2 Key: PrivateKey-2fzYBh3bbWemKxQmMfX6DSuL2BFmDSLQWTvma57xwjQjtf8gFq
// P-Chain Address 1: P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p
// P-Chain Address 1 Key: PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN
// P-Chain Address 2: P-custom16045mxr3s2cjycqe2xfluk304xv3ezhkhsvkpr
// P-Chain Address 2 Key: PrivateKey-2fzYBh3bbWemKxQmMfX6DSuL2BFmDSLQWTvma57xwjQjtf8gFq
2021-11-19 15:38:33 -05:00
// C-Chain Address: 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC
// C-Chain Address Key: 56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027
// The following nodes are validators:
// * NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg
// * NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ
// * NodeID-NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN
// * NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu
// * NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5
func NewDefaultNetwork(
log logging.Logger,
binaryPath string,
2021-11-19 19:39:51 -03:00
) (network.Network, error)
2021-11-19 15:38:33 -05:00
```
2021-11-19 15:55:01 -03:00
2021-12-07 11:50:08 -03:00
The associated pre-defined configuration is also available to users by calling `NewDefaultConfig` function.
2021-11-05 11:07:05 -04:00
## Network Interaction
2021-11-04 20:23:53 -03:00
2021-11-05 11:07:05 -04:00
The network runner allows users to interact with an AvalancheGo network using the `network.Network` interface:
```go
// Network is an abstraction of an Avalanche network
type Network interface {
// Returns a chan that is closed when
// all the nodes in the network are healthy.
// If an error is sent on this channel, at least 1
// node didn't become healthy before the timeout.
// If an error isn't sent on the channel before it
// closes, all the nodes are healthy.
// A stopped network is considered unhealthy.
// Timeout is given by the context parameter.
// [ctx] must eventually be cancelled -- if it isn't, a goroutine is leaked.
Healthy(context.Context) chan error
// Stop all the nodes.
// Returns ErrStopped if Stop() was previously called.
Stop(context.Context) error
// Start a new node with the given config.
// Returns ErrStopped if Stop() was previously called.
AddNode(node.Config) (node.Node, error)
// Stop the node with this name.
// Returns ErrStopped if Stop() was previously called.
RemoveNode(name string) error
// Return the node with this name.
// Returns ErrStopped if Stop() was previously called.
GetNode(name string) (node.Node, error)
// Returns the names of all nodes in this network.
// Returns ErrStopped if Stop() was previously called.
2021-12-13 14:35:01 -06:00
GetNodeNames() ([]string, error)
// TODO add methods
2021-11-05 11:07:05 -04:00
}
2021-11-04 20:23:53 -03:00
```
2021-11-05 11:07:05 -04:00
and allows users to interact with a node using the `node.Node` interface:
2021-11-04 20:23:53 -03:00
2021-11-05 11:07:05 -04:00
```go
// An AvalancheGo node
type Node interface {
// Return this node's name, which is unique
// across all the nodes in its network.
GetName() string
// Return this node's Avalanche node ID.
GetNodeID() ids.ShortID
// Return a client that can be used to make API calls.
GetAPIClient() api.Client
}
2021-11-04 20:23:53 -03:00
```
2021-11-05 11:07:05 -04:00
## Installation
2021-11-04 20:23:53 -03:00
2021-11-05 11:07:05 -04:00
### Download
2021-11-04 20:23:53 -03:00
2021-11-05 11:07:05 -04:00
```sh
2021-11-27 20:11:31 -07:00
git clone https://github.com/ava-labs/avalanche-network-runner.git
2021-11-04 20:23:53 -03:00
```
2021-11-05 11:07:05 -04:00
### Run Unit Tests
2021-11-04 20:23:53 -03:00
2021-11-05 11:07:05 -04:00
Inside the directory cloned above:
2021-11-04 20:23:53 -03:00
2021-11-05 11:07:05 -04:00
```sh
2021-11-04 20:23:53 -03:00
go test ./...
```
2021-12-09 08:55:20 -06:00
### Run an Example
2021-11-05 11:19:08 -03:00
2021-12-09 08:55:20 -06:00
As an example of how to use the network runner, we've included a `main.go` that uses the local implementation of the network runner.
When run, it:
* Creates a local five node Avalanche network and waits for all nodes to become healthy.
* Prints the names of the nodes
* Prints the node ID of one node
* Starts a new node
* Removes an existing node
The network runs until the user provides a SIGINT or SIGTERM.
2021-11-05 11:19:08 -03:00
It assumes:
2021-11-29 12:00:31 -05:00
1. You have the latest AvalancheGo binaries at `$GOPATH/src/github.com/ava-labs/avalanchego/build`. For instructions on setting up AvalancheGo, see [here.](https://github.com/ava-labs/avalanchego)
2021-11-27 20:11:31 -07:00
2. The network runner direcory is at `$GOPATH/src/github.com/ava-labs/avalanche-network-runner`.
2021-11-05 11:19:08 -03:00
2021-11-05 11:07:05 -04:00
To run the demo:
2021-11-05 11:19:08 -03:00
2021-11-05 11:07:05 -04:00
```sh
2021-12-09 08:55:20 -06:00
go run examples/local/indepth/main.go
2021-11-05 11:19:08 -03:00
```
2021-12-09 08:55:20 -06:00
2022-02-23 18:02:09 -08:00
We've also included another example at `examples/local/fivenodenetwork/main.go`, which just starts a local five node network, waits for the nodes to become healthy, and then runs until the user provides a SIGINT or SIGTERM.
# `network-runner` RPC server
**What does `network-runner` do?** The primary focus of [`network-runner`](https://github.com/ava-labs/avalanche-network-runner) is to create a local network, as a test framework for local development.
**Why `network-runner` as a binary?** Previously, each team was still required to write a substantial amount of Go code to integrate with `network-runner`. And the circular dependency on `avalanchego` made it unusable within `avalanchego` itself. Using `network-runner` as a binary (rather than Go package) eliminates the complexity of such dependency management.
**Why `network-runner` needs RPC server?** `network-runner` needs to provide more complex workflow such as replace, restart, inject fail points, etc.. The RPC server will expose basic node operations to enable a separation of concerns such that one team develops test framework, and the other writes test cases and its controlling logic.
**Why gRPC?** The RPC server leads to more modular test components, and gRPC enables greater flexibility. The protocol buffer increases flexibility as we develop more complicated test cases. And gRPC opens up a variety of different approaches for how to write test controller (e.g., Rust). See [`rpcpb/rpc.proto`](./rpcpb/rpc.proto) for service definition.
**Why gRPC gateway?** [gRPC gateway](https://grpc-ecosystem.github.io/grpc-gateway/) exposes gRPC API via HTTP, without us writing any code. Which can be useful if a test controller writer does not want to deal with gRPC.
## Examples
```bash
# to install
cd ${HOME}/go/src/github.com/ava-labs/avalanche-network-runner
go install -v ./cmd/avalanche-network-runner
```
To start the server:
```bash
avalanche-network-runner server \
--log-level debug \
--port=":8080" \
--grpc-gateway-port=":8081"
```
To ping the server:
```bash
curl -X POST -k http://localhost:8081/v1/ping -d ''
# or
2022-02-28 07:24:53 -08:00
avalanche-network-runner ping \
2022-02-23 18:02:09 -08:00
--log-level debug \
--endpoint="0.0.0.0:8080"
```
To start the server:
```bash
# replace with your local path
curl -X POST -k http://localhost:8081/v1/control/start -d '{"execPath":"/Users/gyuho.lee/go/src/github.com/ava-labs/avalanchego/build/avalanchego","whitelistedSubnets":"24tZhrm8j8GCJRE9PomW8FaeqbgGS4UAQjJnqqn8pq5NwYSYV1","logLevel":"INFO"}'
# or
avalanche-network-runner control start \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--avalanchego-path ${HOME}/go/src/github.com/ava-labs/avalanchego/build/avalanchego \
--whitelisted-subnets="24tZhrm8j8GCJRE9PomW8FaeqbgGS4UAQjJnqqn8pq5NwYSYV1"
```
To wait for the cluster health:
```bash
curl -X POST -k http://localhost:8081/v1/control/health -d ''
# or
avalanche-network-runner control health \
--log-level debug \
--endpoint="0.0.0.0:8080"
```
To get the cluster endpoints:
```bash
curl -X POST -k http://localhost:8081/v1/control/uris -d ''
# or
avalanche-network-runner control uris \
--log-level debug \
--endpoint="0.0.0.0:8080"
```
To query the cluster status from the server:
```bash
curl -X POST -k http://localhost:8081/v1/control/status -d ''
# or
avalanche-network-runner control status \
--log-level debug \
--endpoint="0.0.0.0:8080"
```
To stream cluster status:
```bash
avalanche-network-runner control \
--request-timeout=3m \
stream-status \
--push-interval=5s \
--log-level debug \
--endpoint="0.0.0.0:8080"
```
To remove (stop) a node:
```bash
curl -X POST -k http://localhost:8081/v1/control/removenode -d '{"name":"node5"}'
# or
avalanche-network-runner control remove-node \
--request-timeout=3m \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--node-name node5
```
To restart a node, download the test binary:
```bash
# [optional] download a binary to update
# https://github.com/ava-labs/avalanchego/releases
VERSION=1.7.3
GOARCH=$(go env GOARCH)
GOOS=$(go env GOOS)
DOWNLOAD_URL=https://github.com/ava-labs/avalanchego/releases/download/v${VERSION}/avalanchego-linux-${GOARCH}-v${VERSION}.tar.gz
DOWNLOAD_PATH=/tmp/avalanchego.tar.gz
if [[ ${GOOS} == "darwin" ]]; then
DOWNLOAD_URL=https://github.com/ava-labs/avalanchego/releases/download/v${VERSION}/avalanchego-macos-v${VERSION}.zip
DOWNLOAD_PATH=/tmp/avalanchego.zip
fi
rm -rf /tmp/avalanchego-v${VERSION}
rm -rf /tmp/avalanchego-build
rm -f ${DOWNLOAD_PATH}
echo "downloading avalanchego ${VERSION} at ${DOWNLOAD_URL}"
curl -L ${DOWNLOAD_URL} -o ${DOWNLOAD_PATH}
echo "extracting downloaded avalanchego"
if [[ ${GOOS} == "linux" ]]; then
tar xzvf ${DOWNLOAD_PATH} -C /tmp
elif [[ ${GOOS} == "darwin" ]]; then
unzip ${DOWNLOAD_PATH} -d /tmp/avalanchego-build
mv /tmp/avalanchego-build/build /tmp/avalanchego-v${VERSION}
fi
find /tmp/avalanchego-v${VERSION}
```
To restart a node:
```bash
curl -X POST -k http://localhost:8081/v1/control/restartnode -d '{"name":"node1","startRequest":{"execPath":"/tmp/avalanchego-v1.7.3/build/avalanchego",whitelistedSubnets:"",,"logLevel":"INFO"}}'
# or
avalanche-network-runner control restart-node \
--request-timeout=3m \
--log-level debug \
--endpoint="0.0.0.0:8080" \
--node-name node1 \
--avalanchego-path /tmp/avalanchego-v1.7.3/build/avalanchego \
--whitelisted-subnets=""
```
To terminate the cluster:
```bash
curl -X POST -k http://localhost:8081/v1/control/stop -d ''
# or
avalanche-network-runner control stop \
--log-level debug \
--endpoint="0.0.0.0:8080"
```