Skip to content

Commit

Permalink
Merge pull request #87 from ava-labs/add-example-docs
Browse files Browse the repository at this point in the history
Add more docs
  • Loading branch information
sukantoraymond authored Jul 2, 2024
2 parents 0e3a994 + 957f93b commit 9e37b3c
Show file tree
Hide file tree
Showing 15 changed files with 648 additions and 324 deletions.
147 changes: 144 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ The official Avalanche Tooling Go SDK library.
*** Please note that this SDK is in experimental mode, major changes to the SDK are to be expected
in between releases ***

Current version (v0.1.0) currently only supports Create Subnet and Create Blockchain in a
Subnet in Fuji / Mainnet.
Current version (v0.2.0) currently supports:
- Create Subnet and Create Blockchain in a Subnet in Fuji / Mainnet.
- Create Avalanche Node (Validator / API / Monitoring / Load Test Node) & install all required
dependencies (AvalancheGo, gcc, Promtail, Grafana, etc).

Currently, only stored keys are supported for transaction building and signing, ledger support is
coming soon.
Expand All @@ -15,7 +17,7 @@ Future SDK releases will contain the following features:
- Additional Subnet SDK features (i.e. Adding Validators to a Subnet, Custom Subnets, Exporting Subnets)
- Ledger Support
- Teleporter Support
- Nodes SDK (Creating and setting up Avalanche Validator Nodes and API Nodes )
- Nodes SDK (Have Avalanche nodes validate a Subnet & Primary Network)

## Getting Started

Expand Down Expand Up @@ -140,3 +142,142 @@ func getDefaultSubnetEVMGenesis() subnet.SubnetParams {
}
}
```

### Nodes SDK Example

This example shows how to create Avalanche Validator Nodes (SDK function for nodes to start
validating Primary Network / Subnet will be available in the next Avalanche Tooling SDK release).

This examples also shows how to create an Avalanche Monitoring Node, which enables you to have a
centralized Grafana Dashboard where you can view metrics relevant to any Validator & API nodes that
the monitoring node is linked to as well as a centralized logs for the X/P/C Chain and Subnet logs
for the Validator & API nodes. An example on how the dashboard and logs look like can be found at https://docs.avax.network/tooling/cli-create-nodes/create-a-validator-aws

```go
package main

import (
"context"
"fmt"
"time"

"github.com/ava-labs/avalanche-tooling-sdk-go/avalanche"
awsAPI "github.com/ava-labs/avalanche-tooling-sdk-go/cloud/aws"
"github.com/ava-labs/avalanche-tooling-sdk-go/node"
"github.com/ava-labs/avalanche-tooling-sdk-go/utils"
)

func CreateNodes() {
ctx := context.Background()

// Get the default cloud parameters for AWS
cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud)
if err != nil {
panic(err)
}

securityGroupName := "SECURITY_GROUP_NAME"
// Create a new security group in AWS if you do not currently have one in the selected
// AWS region.
sgID, err := awsAPI.CreateSecurityGroup(ctx, securityGroupName, cp.AWSConfig.AWSProfile, cp.Region)
if err != nil {
panic(err)
}
// Set the security group we are using when creating our Avalanche Nodes
cp.AWSConfig.AWSSecurityGroupID = sgID

keyPairName := "KEY_PAIR_NAME"
sshPrivateKeyPath := utils.ExpandHome("PRIVATE_KEY_FILEPATH")
// Create a new AWS SSH key pair if you do not currently have one in your selected AWS region.
// Note that the created key pair can only be used in the region that it was created in.
// The private key to the created key pair will be stored in the filepath provided in
// sshPrivateKeyPath.
if err := awsAPI.CreateSSHKeyPair(ctx, cp.AWSConfig.AWSProfile, cp.Region, keyPairName, sshPrivateKeyPath); err != nil {
panic(err)
}
// Set the key pair we are using when creating our Avalanche Nodes
cp.AWSConfig.AWSKeyPair = keyPairName

// Avalanche-CLI is installed in nodes to enable them to join subnets as validators
// Avalanche-CLI dependency by Avalanche nodes will be deprecated in the next release
// of Avalanche Tooling SDK
const (
avalancheGoVersion = "v1.11.8"
avalancheCliVersion = "v1.6.2"
)

// Create two new Avalanche Validator nodes on Fuji Network on AWS without Elastic IPs
// attached. Once CreateNodes is completed, the validators will begin bootstrapping process
// to Primary Network in Fuji Network. Nodes need to finish bootstrapping process
// before they can validate Avalanche Primary Network / Subnet.
//
// SDK function for nodes to start validating Primary Network / Subnet will be available
// in the next Avalanche Tooling SDK release.
hosts, err := node.CreateNodes(ctx,
&node.NodeParams{
CloudParams: cp,
Count: 2,
Roles: []node.SupportedRole{node.Validator},
Network: avalanche.FujiNetwork(),
AvalancheGoVersion: avalancheGoVersion,
AvalancheCliVersion: avalancheCliVersion,
UseStaticIP: false,
SSHPrivateKeyPath: sshPrivateKeyPath,
})
if err != nil {
panic(err)
}

const (
sshTimeout = 120 * time.Second
sshCommandTimeout = 10 * time.Second
)

// Examples showing how to run ssh commands on the created nodes
for _, h := range hosts {
// Wait for the host to be ready (only needs to be done once for newly created nodes)
fmt.Println("Waiting for SSH shell")
if err := h.WaitForSSHShell(sshTimeout); err != nil {
panic(err)
}
fmt.Println("SSH shell ready to execute commands")
// Run a command on the host
if output, err := h.Commandf(nil, sshCommandTimeout, "echo 'Hello, %s!'", "World"); err != nil {
panic(err)
} else {
fmt.Println(string(output))
}
// sleep for 10 seconds allowing AvalancheGo container to start
time.Sleep(10 * time.Second)
// check if avalanchego is running
if output, err := h.Commandf(nil, sshCommandTimeout, "docker ps"); err != nil {
panic(err)
} else {
fmt.Println(string(output))
}
}

// Create a monitoring node.
// Monitoring node enables you to have a centralized Grafana Dashboard where you can view
// metrics relevant to any Validator & API nodes that the monitoring node is linked to as well
// as a centralized logs for the X/P/C Chain and Subnet logs for the Validator & API nodes.
// An example on how the dashboard and logs look like can be found at https://docs.avax.network/tooling/cli-create-nodes/create-a-validator-aws
monitoringHosts, err := node.CreateNodes(ctx,
&node.NodeParams{
CloudParams: cp,
Count: 1,
Roles: []node.SupportedRole{node.Monitor},
UseStaticIP: false,
SSHPrivateKeyPath: sshPrivateKeyPath,
})
if err != nil {
panic(err)
}

// Link the 2 validator nodes previously created with the monitoring host so that
// the monitoring host can start tracking the validator nodes metrics and collecting their logs
if err := monitoringHosts[0].MonitorNodes(ctx, hosts, ""); err != nil {
panic(err)
}
}
```
27 changes: 0 additions & 27 deletions avalanche/avalanche.go

This file was deleted.

13 changes: 0 additions & 13 deletions avalanche/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,12 @@ const (
Undefined NetworkKind = iota
Mainnet
Fuji
Local
Devnet
)

const (
LocalNetworkID = 1337
FujiAPIEndpoint = "https://api.avax-test.network"
MainnetAPIEndpoint = "https://api.avax.network"
LocalAPIEndpoint = "http://127.0.0.1:9650"
)

func (nk NetworkKind) String() string {
Expand All @@ -28,8 +25,6 @@ func (nk NetworkKind) String() string {
return "Mainnet"
case Fuji:
return "Fuji"
case Local:
return "Local Network"
case Devnet:
return "Devnet"
}
Expand All @@ -46,8 +41,6 @@ var UndefinedNetwork = Network{}

func (n Network) HRP() string {
switch n.ID {
case constants.LocalID:
return constants.LocalHRP
case constants.FujiID:
return constants.FujiHRP
case constants.MainnetID:
Expand All @@ -63,8 +56,6 @@ func NetworkFromNetworkID(networkID uint32) Network {
return MainnetNetwork()
case constants.FujiID:
return FujiNetwork()
case LocalNetworkID:
return LocalNetwork()
}
return UndefinedNetwork
}
Expand All @@ -77,10 +68,6 @@ func NewNetwork(kind NetworkKind, id uint32, endpoint string) Network {
}
}

func LocalNetwork() Network {
return NewNetwork(Local, LocalNetworkID, LocalAPIEndpoint)
}

func FujiNetwork() Network {
return NewNetwork(Fuji, constants.FujiID, FujiAPIEndpoint)
}
Expand Down
1 change: 0 additions & 1 deletion avalanche/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ type VMType string

const (
SubnetEvm = "Subnet-EVM"
CustomVM = "Custom"
)

func (v VMType) RepoName() string {
Expand Down
76 changes: 74 additions & 2 deletions cloud/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,10 @@ func (c *AwsCloud) CreateEC2Instances(count int, amiID, instanceType, keyName, s
{
ResourceType: types.ResourceTypeInstance,
Tags: []types.Tag{
{
Key: aws.String("Name"),
Value: aws.String("avalanche-tooling-sdk-node"),
},
{
Key: aws.String("Managed-By"),
Value: aws.String("avalanche-cli"),
Expand Down Expand Up @@ -532,8 +536,15 @@ func (c *AwsCloud) CheckKeyPairExists(kpName string) (bool, error) {
return true, nil
}

// GetUbuntuAMIID returns the ID of the latest Ubuntu Amazon Machine Image (AMI).
func (c *AwsCloud) GetUbuntuAMIID(arch string, ubuntuVerLTS string) (string, error) {
// GetAvalancheUbuntuAMIID returns the ID of the latest Ubuntu Amazon Machine Image (AMI) published
// by Avalanche Tooling on AWS.
//
// Avalanche Tooling publishes our own Ubuntu 20.04 Machine Image called Avalanche-CLI
// Ubuntu 20.04 Docker for both arm64 and amd64 architecture.
// A benefit to using Avalanche-CLI Ubuntu 20.04 Docker is that it has all the dependencies
// that an Avalanche Node requires (AvalancheGo, gcc, go, etc), thereby decreasing in massive
// reduction in the time required to provision a node.
func (c *AwsCloud) GetAvalancheUbuntuAMIID(arch string, ubuntuVerLTS string) (string, error) {
if !utils.ArchSupported(arch) {
return "", fmt.Errorf("unsupported architecture: %s", arch)
}
Expand Down Expand Up @@ -744,6 +755,55 @@ func (c *AwsCloud) ChangeInstanceType(instanceID, instanceType string) error {
return nil
}

// CreateSecurityGroup creates a new Security Group in AWS using the specified AWS profile and
// region.
//
// ctx: The context.Context object for the request.
// awsProfile: The AWS profile to use for the request.
// awsRegion: The AWS region to use for the request.
// Returns the ID of the created security group and an error, if any.
func CreateSecurityGroup(ctx context.Context, securityGroupName, awsProfile, awsRegion string) (string, error) {
ec2Svc, err := NewAwsCloud(
ctx,
awsProfile,
awsRegion,
)
if err != nil {
return "", err
}
// detect user IP address
userIPAddress, err := utils.GetUserIPAddress()
if err != nil {
return "", err
}
return ec2Svc.SetupSecurityGroup(userIPAddress, securityGroupName)
}

// CreateSSHKeyPair creates a new SSH key pair for AWS in the specified AWS region.
// The private key to the created key pair will be downloaded and stored in the filepath provided
// in sshPrivateKeyPath.
// createSSHKeyPair will return an error if the filepath sshPrivateKeyPath is not empty
//
// ctx: The context for the request.
// awsProfile: The AWS profile to use for the request.
// awsRegion: The AWS region to use for the request.
// sshPrivateKeyPath: The path to save the SSH private key.
// Returns an error if unable to create the key pair.
func CreateSSHKeyPair(ctx context.Context, awsProfile string, awsRegion string, keyPairName string, sshPrivateKeyPath string) error {
if utils.FileExists(sshPrivateKeyPath) {
return fmt.Errorf("ssh private key path %s is not empty", sshPrivateKeyPath)
}
ec2Svc, err := NewAwsCloud(
ctx,
awsProfile,
awsRegion,
)
if err != nil {
return err
}
return ec2Svc.CreateAndDownloadKeyPair(keyPairName, sshPrivateKeyPath)
}

func (c *AwsCloud) AddMonitoringSecurityGroupRule(monitoringHostPublicIP, securityGroupName string) error {
securityGroupExists, sg, err := c.CheckSecurityGroupExists(securityGroupName)
if err != nil {
Expand All @@ -766,3 +826,15 @@ func (c *AwsCloud) AddMonitoringSecurityGroupRule(monitoringHostPublicIP, securi
}
return nil
}

func WhitelistMonitoringAccess(ctx context.Context, awsProfile string, awsRegion string, awsSG string, monitoringIP string) error {
ec2Svc, err := NewAwsCloud(
ctx,
awsProfile,
awsRegion,
)
if err != nil {
return err
}
return ec2Svc.AddMonitoringSecurityGroupRule(monitoringIP, awsSG)
}
13 changes: 9 additions & 4 deletions cloud/gcp/gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,15 @@ func (c *GcpCloud) SetupInstances(
return instances, nil
}

// // Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// // See the file LICENSE for licensing terms.

func (c *GcpCloud) GetUbuntuimageID() (string, error) {
// GetAvalancheUbuntuAMIID returns the ID of the latest Ubuntu Amazon Machine Image (AMI) published
// by Avalanche Tooling on GCP.
//
// Avalanche Tooling publishes our own Ubuntu 20.04 Machine Image called Avalanche-CLI
// Ubuntu 20.04 Docker for both arm64 and amd64 architecture.
// A benefit to using Avalanche-CLI Ubuntu 20.04 Docker is that it has all the dependencies
// that an Avalanche Node requires (AvalancheGo, gcc, go, etc), thereby decreasing in massive
// reduction in the time required to provision a node.
func (c *GcpCloud) GetAvalancheUbuntuAMIID() (string, error) {
imageListCall := c.gcpClient.Images.List(constants.GCPDefaultImageProvider).Filter(constants.GCPImageFilter)
imageList, err := imageListCall.Do()
if err != nil {
Expand Down
Loading

0 comments on commit 9e37b3c

Please sign in to comment.