Skip to content
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
7 changes: 4 additions & 3 deletions cmd/networkcmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ func Start(flags StartFlags, printEndpoints bool) error {
ctx, cancel := localnet.GetLocalNetworkDefaultContext()
defer cancel()
if _, err := localnet.TmpNetLoad(ctx, app.Log, networkDir, avalancheGoBinPath); err != nil {
_ = localnet.TmpNetStop(networkDir)
return err
}
// save network directory
Expand Down Expand Up @@ -211,7 +212,7 @@ func Start(flags StartFlags, printEndpoints bool) error {
// create network
ctx, cancel := localnet.GetLocalNetworkDefaultContext()
defer cancel()
_, err = localnet.TmpNetCreate(
if _, err := localnet.TmpNetCreate(
ctx,
app.Log,
networkDir,
Expand All @@ -225,8 +226,8 @@ func Start(flags StartFlags, printEndpoints bool) error {
defaultFlags,
nodes,
true,
)
if err != nil {
); err != nil {
_ = localnet.TmpNetStop(networkDir)
return err
}
// save network directory
Expand Down
15 changes: 15 additions & 0 deletions pkg/localnet/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
package localnet

import (
"context"
"errors"
"fmt"
"os"

Expand Down Expand Up @@ -78,6 +80,19 @@ func TrackSubnet(
perNodeBlockchainConfig,
wallet,
); err != nil {
if errors.Is(err, context.DeadlineExceeded) {
printFunc("")
printFunc("A context timeout has occurred while trying to bootstrap the blockchain.")
printFunc("")
logPaths, _ := GetTmpNetAvailableLogs(networkDir, blockchainID, false)
if len(logPaths) != 0 {
printFunc("Please check this log files for more information on the error cause:")
for _, logPath := range logPaths {
printFunc(" " + logPath)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add unit test for helpers_test.go where we intentionally cause error and verify that we get the expected logs dir

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

added test for GetTmpNetAvailableLogs

}
printFunc("")
}
}
return err
}
ux.Logger.GreenCheckmarkToUser("%s successfully tracking %s", networkModel.Name(), blockchainName)
Expand Down
31 changes: 31 additions & 0 deletions pkg/localnet/tmpnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -1094,3 +1094,34 @@ func GetTmpNetNodeURIsWithFix(
}
return utils.Map(network.GetNodeURIs(), func(nodeURI tmpnet.NodeURI) string { return nodeURI.URI }), nil
}

// Get paths for most important avalanchego logs that are present on the network nodes
func GetTmpNetAvailableLogs(
networkDir string,
blockchainID ids.ID,
includeCChain bool,
) ([]string, error) {
network, err := GetTmpNetNetwork(networkDir)
if err != nil {
return nil, err
}
prefixes := []string{}
if blockchainID != ids.Empty {
prefixes = append(prefixes, blockchainID.String())
}
if includeCChain {
prefixes = append(prefixes, "C")
}
prefixes = append(prefixes, "P")
prefixes = append(prefixes, "main")
logPaths := []string{}
for _, node := range network.Nodes {
for _, prefix := range prefixes {
logPath := filepath.Join(networkDir, node.NodeID.String(), "logs", prefix+".log")
if utils.FileExists(logPath) {
logPaths = append(logPaths, utils.ReplaceUserHomeWithTilde(logPath))
}
}
}
return logPaths, nil
}
162 changes: 162 additions & 0 deletions pkg/localnet/tmpnet_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright (C) 2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package localnet

import (
"context"
"os"
"path/filepath"
"testing"

"github.com/ava-labs/avalanche-cli/pkg/application"
"github.com/ava-labs/avalanche-cli/pkg/config"
"github.com/ava-labs/avalanche-cli/pkg/constants"
"github.com/ava-labs/avalanche-cli/pkg/prompts"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/logging"

"github.com/stretchr/testify/require"
)

func createFile(t *testing.T, path string) {
f, err := os.Create(path)
require.NoError(t, err)
err = f.Close()
require.NoError(t, err)
}

func TestGetTmpNetAvailableLogs(t *testing.T) {
app := &application.Avalanche{}
appDir, err := os.MkdirTemp(os.TempDir(), "cli-app-test")
require.NoError(t, err)
app.Setup(appDir, logging.NoLog{}, config.New(), "", prompts.NewPrompter(), application.NewDownloader(), nil)
networkID, unparsedGenesis, upgradeBytes, defaultFlags, nodes, err := GetDefaultNetworkConf(2)
require.NoError(t, err)
networkDir, err := os.MkdirTemp(os.TempDir(), "cli-tmpnet-test")
require.NoError(t, err)
_, err = TmpNetCreate(
context.Background(),
app.Log,
networkDir,
"",
"",
networkID,
nil,
nil,
unparsedGenesis,
upgradeBytes,
defaultFlags,
nodes,
false,
)
require.NoError(t, err)
// no logs yet
logPaths, err := GetTmpNetAvailableLogs(networkDir, ids.Empty, false)
require.NoError(t, err)
require.Equal(t, []string{}, logPaths)
// default network
node1ID := "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg"
node2ID := "NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ"
node1Logs := filepath.Join(networkDir, node1ID, "logs")
node2Logs := filepath.Join(networkDir, node2ID, "logs")
err = os.MkdirAll(node1Logs, constants.DefaultPerms755)
require.NoError(t, err)
err = os.MkdirAll(node2Logs, constants.DefaultPerms755)
require.NoError(t, err)
// add main log
createFile(t, filepath.Join(node1Logs, "main.log"))
createFile(t, filepath.Join(node2Logs, "main.log"))
logPaths, err = GetTmpNetAvailableLogs(networkDir, ids.Empty, false)
require.NoError(t, err)
require.Equal(t, []string{
filepath.Join(node1Logs, "main.log"),
filepath.Join(node2Logs, "main.log"),
}, logPaths)
// add P chain log
createFile(t, filepath.Join(node1Logs, "P.log"))
createFile(t, filepath.Join(node2Logs, "P.log"))
logPaths, err = GetTmpNetAvailableLogs(networkDir, ids.Empty, false)
require.NoError(t, err)
require.Equal(t, []string{
filepath.Join(node1Logs, "P.log"),
filepath.Join(node1Logs, "main.log"),
filepath.Join(node2Logs, "P.log"),
filepath.Join(node2Logs, "main.log"),
}, logPaths)
// gather C chain when no files are present
logPaths, err = GetTmpNetAvailableLogs(networkDir, ids.Empty, true)
require.NoError(t, err)
require.Equal(t, []string{
filepath.Join(node1Logs, "P.log"),
filepath.Join(node1Logs, "main.log"),
filepath.Join(node2Logs, "P.log"),
filepath.Join(node2Logs, "main.log"),
}, logPaths)
// gather C chain when files are present
createFile(t, filepath.Join(node1Logs, "C.log"))
createFile(t, filepath.Join(node2Logs, "C.log"))
logPaths, err = GetTmpNetAvailableLogs(networkDir, ids.Empty, true)
require.NoError(t, err)
require.Equal(t, []string{
filepath.Join(node1Logs, "C.log"),
filepath.Join(node1Logs, "P.log"),
filepath.Join(node1Logs, "main.log"),
filepath.Join(node2Logs, "C.log"),
filepath.Join(node2Logs, "P.log"),
filepath.Join(node2Logs, "main.log"),
}, logPaths)
// don't gather C chain when files are present
logPaths, err = GetTmpNetAvailableLogs(networkDir, ids.Empty, false)
require.NoError(t, err)
require.Equal(t, []string{
filepath.Join(node1Logs, "P.log"),
filepath.Join(node1Logs, "main.log"),
filepath.Join(node2Logs, "P.log"),
filepath.Join(node2Logs, "main.log"),
}, logPaths)
// gather blockchain when no files are present
blockchainID := ids.GenerateTestID()
logPaths, err = GetTmpNetAvailableLogs(networkDir, blockchainID, false)
require.NoError(t, err)
require.Equal(t, []string{
filepath.Join(node1Logs, "P.log"),
filepath.Join(node1Logs, "main.log"),
filepath.Join(node2Logs, "P.log"),
filepath.Join(node2Logs, "main.log"),
}, logPaths)
// gather blockchain when files are present
createFile(t, filepath.Join(node1Logs, blockchainID.String()+".log"))
createFile(t, filepath.Join(node2Logs, blockchainID.String()+".log"))
logPaths, err = GetTmpNetAvailableLogs(networkDir, blockchainID, false)
require.NoError(t, err)
require.Equal(t, []string{
filepath.Join(node1Logs, blockchainID.String()+".log"),
filepath.Join(node1Logs, "P.log"),
filepath.Join(node1Logs, "main.log"),
filepath.Join(node2Logs, blockchainID.String()+".log"),
filepath.Join(node2Logs, "P.log"),
filepath.Join(node2Logs, "main.log"),
}, logPaths)
// don't gather blockchain when files are present
logPaths, err = GetTmpNetAvailableLogs(networkDir, ids.Empty, false)
require.NoError(t, err)
require.Equal(t, []string{
filepath.Join(node1Logs, "P.log"),
filepath.Join(node1Logs, "main.log"),
filepath.Join(node2Logs, "P.log"),
filepath.Join(node2Logs, "main.log"),
}, logPaths)
// gather all files are present
logPaths, err = GetTmpNetAvailableLogs(networkDir, blockchainID, true)
require.NoError(t, err)
require.Equal(t, []string{
filepath.Join(node1Logs, blockchainID.String()+".log"),
filepath.Join(node1Logs, "C.log"),
filepath.Join(node1Logs, "P.log"),
filepath.Join(node1Logs, "main.log"),
filepath.Join(node2Logs, blockchainID.String()+".log"),
filepath.Join(node2Logs, "C.log"),
filepath.Join(node2Logs, "P.log"),
filepath.Join(node2Logs, "main.log"),
}, logPaths)
}
10 changes: 10 additions & 0 deletions pkg/utils/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/ava-labs/avalanche-cli/pkg/constants"
sdkutils "github.com/ava-labs/avalanche-cli/sdk/utils"
Expand Down Expand Up @@ -72,6 +73,15 @@ func ExpandHome(path string) string {
return path
}

// ReplaceUserHomeWithTilde replaces user home directory with ~
func ReplaceUserHomeWithTilde(path string) string {
home, _ := os.UserHomeDir()
if strings.HasPrefix(path, home) {
path = "~" + strings.TrimPrefix(path, home)
}
return path
}

// FileCopy copies a file from src to dst.
func FileCopy(src string, dst string) error {
if !FileExists(src) {
Expand Down
9 changes: 8 additions & 1 deletion sdk/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package utils

import (
"context"
"os"
"os/signal"
"sort"
"time"

Expand Down Expand Up @@ -48,5 +50,10 @@ func GetAPILargeContext() (context.Context, context.CancelFunc) {

// Timed Context
func GetTimedContext(timeout time.Duration) (context.Context, context.CancelFunc) {
return context.WithTimeout(context.Background(), timeout)
parent, sigCancel := signal.NotifyContext(context.Background(), os.Interrupt)
ctx, timeCancel := context.WithTimeout(parent, timeout)
return ctx, func() {
sigCancel()
timeCancel()
}
}
Loading