diff --git a/cmd/blockchaincmd/deploy.go b/cmd/blockchaincmd/deploy.go index 78e19cf7a..11e3239d4 100644 --- a/cmd/blockchaincmd/deploy.go +++ b/cmd/blockchaincmd/deploy.go @@ -11,6 +11,8 @@ import ( "strings" "time" + "github.com/ava-labs/avalanche-cli/pkg/node" + "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" "github.com/ethereum/go-ethereum/common" @@ -684,62 +686,59 @@ func deployBlockchain(cmd *cobra.Command, args []string) error { return err } - if false { - chainSpec := contract.ChainSpec{ + clusterName, err := node.GetClusterNameFromList(app) + if err != nil { + return err + } + + if err = node.SyncSubnet(app, clusterName, blockchainName, true, nil); err != nil { + return err + } + + if err := node.WaitForHealthyCluster(app, clusterName, node.HealthCheckTimeout, node.HealthCheckPoolTime); err != nil { + return err + } + + chainSpec := contract.ChainSpec{ + BlockchainName: blockchainName, + } + _, genesisPrivateKey, err := contract.GetEVMSubnetPrefundedKey( + app, + network, + chainSpec, + ) + if err != nil { + return err + } + rpcURL, _, err := contract.GetBlockchainEndpoints( + app, + network, + chainSpec, + true, + false, + ) + if err != nil { + return err + } + if err := validatormanager.SetupPoA( + app, + network, + rpcURL, + contract.ChainSpec{ BlockchainName: blockchainName, - } - genesisAddress, genesisPrivateKey, err := contract.GetEVMSubnetPrefundedKey( - app, - network, - chainSpec, - ) - if err != nil { - return err - } - privateKey, err := privateKeyFlags.GetPrivateKey(app, genesisPrivateKey) - if err != nil { - return err - } - if privateKey == "" { - privateKey, err = prompts.PromptPrivateKey( - app.Prompt, - "Which key to you want to use to pay for initializing Validator Manager contract? (Uses Blockchain gas token)", - app.GetKeyDir(), - app.GetKey, - genesisAddress, - genesisPrivateKey, - ) - if err != nil { - return err - } - } - rpcURL, _, err := contract.GetBlockchainEndpoints( - app, - network, - chainSpec, - true, - false, - ) - if err != nil { - return err - } - if err := validatormanager.SetupPoA( - app, - network, - rpcURL, - contract.ChainSpec{ - BlockchainName: blockchainName, - }, - privateKey, - common.HexToAddress(sidecar.PoAValidatorManagerOwner), - avaGoBootstrapValidators, - ); err != nil { - return err - } - ux.Logger.GreenCheckmarkToUser("Subnet is successfully converted into Subnet Only Validator") + }, + genesisPrivateKey, + common.HexToAddress(sidecar.PoAValidatorManagerOwner), + avaGoBootstrapValidators, + ); err != nil { + return err + } + ux.Logger.GreenCheckmarkToUser("L1 is successfully converted to sovereign blockchain") + } else { + if err := app.UpdateSidecarNetworks(&sidecar, network, subnetID, blockchainID, "", "", nil); err != nil { + return err } } - flags := make(map[string]string) flags[constants.MetricsNetwork] = network.Name() metrics.HandleTracking(cmd, constants.MetricsSubnetDeployCommand, app, flags) diff --git a/cmd/nodecmd/wiz.go b/cmd/nodecmd/wiz.go index fb106b3e1..6256f9721 100644 --- a/cmd/nodecmd/wiz.go +++ b/cmd/nodecmd/wiz.go @@ -257,7 +257,7 @@ func wiz(cmd *cobra.Command, args []string) error { } } - if err := waitForHealthyCluster(clusterName, healthCheckTimeout, healthCheckPoolTime); err != nil { + if err := node.WaitForHealthyCluster(app, clusterName, healthCheckTimeout, healthCheckPoolTime); err != nil { return err } @@ -349,7 +349,7 @@ func wiz(cmd *cobra.Command, args []string) error { if err := syncSubnet(cmd, []string{clusterName, subnetName}); err != nil { return err } - if err := waitForHealthyCluster(clusterName, healthCheckTimeout, healthCheckPoolTime); err != nil { + if err := node.WaitForHealthyCluster(app, clusterName, healthCheckTimeout, healthCheckPoolTime); err != nil { return err } blockchainID := sc.Networks[network.Name()].BlockchainID @@ -636,57 +636,6 @@ func checkRPCCompatibility( return node.CheckHostsAreRPCCompatible(app, hosts, subnetName) } -func waitForHealthyCluster( - clusterName string, - timeout time.Duration, - poolTime time.Duration, -) error { - ux.Logger.PrintToUser("") - ux.Logger.PrintToUser("Waiting for node(s) in cluster %s to be healthy...", clusterName) - clustersConfig, err := app.LoadClustersConfig() - if err != nil { - return err - } - cluster, ok := clustersConfig.Clusters[clusterName] - if !ok { - return fmt.Errorf("cluster %s does not exist", clusterName) - } - allHosts, err := ansible.GetInventoryFromAnsibleInventoryFile(app.GetAnsibleInventoryDirPath(clusterName)) - if err != nil { - return err - } - hosts := cluster.GetValidatorHosts(allHosts) // exlude api nodes - defer node.DisconnectHosts(hosts) - startTime := time.Now() - spinSession := ux.NewUserSpinner() - spinner := spinSession.SpinToUser("Checking if node(s) are healthy...") - for { - unhealthyNodes, err := node.GetUnhealthyNodes(hosts) - if err != nil { - ux.SpinFailWithError(spinner, "", err) - return err - } - if len(unhealthyNodes) == 0 { - ux.SpinComplete(spinner) - spinSession.Stop() - ux.Logger.GreenCheckmarkToUser("Nodes healthy after %d seconds", uint32(time.Since(startTime).Seconds())) - return nil - } - if time.Since(startTime) > timeout { - ux.SpinFailWithError(spinner, "", fmt.Errorf("cluster not healthy after %d seconds", uint32(timeout.Seconds()))) - spinSession.Stop() - ux.Logger.PrintToUser("") - ux.Logger.RedXToUser("Unhealthy Nodes") - for _, failedNode := range unhealthyNodes { - ux.Logger.PrintToUser(" " + failedNode) - } - ux.Logger.PrintToUser("") - return fmt.Errorf("cluster not healthy after %d seconds", uint32(timeout.Seconds())) - } - time.Sleep(poolTime) - } -} - func waitForSubnetValidators( network models.Network, clusterName string, diff --git a/go.mod b/go.mod index 3776779d4..743d658dc 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.8 require ( github.com/ava-labs/apm v1.0.0 github.com/ava-labs/avalanche-network-runner v1.8.4-0.20241005224128-cc3c07bb1344 - github.com/ava-labs/avalanchego v1.12.0-initial-poc.3 + github.com/ava-labs/avalanchego v1.12.0-initial-poc.5 github.com/ava-labs/awm-relayer v1.4.1-0.20241003162124-807fd305670f github.com/ava-labs/coreth v0.13.8 github.com/ava-labs/subnet-evm v0.6.10 @@ -115,7 +115,6 @@ require ( github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect github.com/gliderlabs/ssh v0.3.7 // indirect - github.com/go-cmd/cmd v1.4.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-logr/logr v1.4.2 // indirect diff --git a/go.sum b/go.sum index f7f98a76c..fec835498 100644 --- a/go.sum +++ b/go.sum @@ -85,8 +85,8 @@ github.com/ava-labs/apm v1.0.0 h1:6FwozH67hEkbWVsOXNZGexBy5KLpNeYucN9zcFUHv+Q= github.com/ava-labs/apm v1.0.0/go.mod h1:TJL7pTlZNvQatsQPsLUtDHApEwVZ/qS7iSNtRFU83mc= github.com/ava-labs/avalanche-network-runner v1.8.4-0.20241005224128-cc3c07bb1344 h1:wD/rBr+QKztcKtRtBNqPjzMhwcxnVcuJ3GT62DdgS2Q= github.com/ava-labs/avalanche-network-runner v1.8.4-0.20241005224128-cc3c07bb1344/go.mod h1:l4QzFnujbyyyeq6oBQ4F6sw9TrTQCjD2V4vUd7ZBCCo= -github.com/ava-labs/avalanchego v1.12.0-initial-poc.3 h1:JfVooBCdMzpeGUT9/phJNl2GHflkGehlMJokXeWKa2A= -github.com/ava-labs/avalanchego v1.12.0-initial-poc.3/go.mod h1:qSHmog3wMVjo/ruIAQo0ppXAilyni07NIu5K88RyhWE= +github.com/ava-labs/avalanchego v1.12.0-initial-poc.5 h1:gW4xAqZNvkA4gP8M9yDyd7YUzuwfQbbCR+hgd1ztOto= +github.com/ava-labs/avalanchego v1.12.0-initial-poc.5/go.mod h1:qSHmog3wMVjo/ruIAQo0ppXAilyni07NIu5K88RyhWE= github.com/ava-labs/awm-relayer v1.4.1-0.20241003162124-807fd305670f h1:YUQF1wQJeEcTMC5W/OrwgSFTFMS4zeCM8O02rLeEDow= github.com/ava-labs/awm-relayer v1.4.1-0.20241003162124-807fd305670f/go.mod h1:K01Md6zPkOFRWeQyxmZ/t9HJfoNgUGqa1L8rOp35GXw= github.com/ava-labs/coreth v0.13.8 h1:f14X3KgwHl9LwzfxlN6S4bbn5VA2rhEsNnHaRLSTo/8= @@ -332,8 +332,6 @@ github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7 github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-cmd/cmd v1.4.1 h1:JUcEIE84v8DSy02XTZpUDeGKExk2oW3DA10hTjbQwmc= -github.com/go-cmd/cmd v1.4.1/go.mod h1:tbBenttXtZU4c5djS1o7PWL5pd2xAr5sIqH1kGdNiRc= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= @@ -368,8 +366,6 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= -github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= diff --git a/pkg/node/helper.go b/pkg/node/helper.go index 0370bcb72..d97fec3f9 100644 --- a/pkg/node/helper.go +++ b/pkg/node/helper.go @@ -6,6 +6,9 @@ import ( "encoding/json" "fmt" "sync" + "time" + + "github.com/ava-labs/avalanche-cli/pkg/ansible" "github.com/ava-labs/avalanche-cli/pkg/application" "github.com/ava-labs/avalanche-cli/pkg/constants" @@ -16,6 +19,11 @@ import ( "github.com/ava-labs/avalanchego/api/info" ) +const ( + HealthCheckPoolTime = 60 * time.Second + HealthCheckTimeout = 3 * time.Minute +) + func AuthorizedAccessFromSettings(app *application.Avalanche) bool { return app.Conf.GetConfigBoolValue(constants.ConfigAuthorizeCloudAccessKey) } @@ -218,3 +226,73 @@ func parseHealthyOutput(byteValue []byte) (bool, error) { } return false, fmt.Errorf("unable to parse node healthy status") } + +func WaitForHealthyCluster( + app *application.Avalanche, + clusterName string, + timeout time.Duration, + poolTime time.Duration, +) error { + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Waiting for node(s) in cluster %s to be healthy...", clusterName) + clustersConfig, err := app.LoadClustersConfig() + if err != nil { + return err + } + cluster, ok := clustersConfig.Clusters[clusterName] + if !ok { + return fmt.Errorf("cluster %s does not exist", clusterName) + } + allHosts, err := ansible.GetInventoryFromAnsibleInventoryFile(app.GetAnsibleInventoryDirPath(clusterName)) + if err != nil { + return err + } + hosts := cluster.GetValidatorHosts(allHosts) // exlude api nodes + defer DisconnectHosts(hosts) + startTime := time.Now() + spinSession := ux.NewUserSpinner() + spinner := spinSession.SpinToUser("Checking if node(s) are healthy...") + for { + unhealthyNodes, err := GetUnhealthyNodes(hosts) + if err != nil { + ux.SpinFailWithError(spinner, "", err) + return err + } + if len(unhealthyNodes) == 0 { + ux.SpinComplete(spinner) + spinSession.Stop() + ux.Logger.GreenCheckmarkToUser("Nodes healthy after %d seconds", uint32(time.Since(startTime).Seconds())) + return nil + } + if time.Since(startTime) > timeout { + ux.SpinFailWithError(spinner, "", fmt.Errorf("cluster not healthy after %d seconds", uint32(timeout.Seconds()))) + spinSession.Stop() + ux.Logger.PrintToUser("") + ux.Logger.RedXToUser("Unhealthy Nodes") + for _, failedNode := range unhealthyNodes { + ux.Logger.PrintToUser(" " + failedNode) + } + ux.Logger.PrintToUser("") + return fmt.Errorf("cluster not healthy after %d seconds", uint32(timeout.Seconds())) + } + time.Sleep(poolTime) + } +} + +func GetClusterNameFromList(app *application.Avalanche) (string, error) { + clusterNames, err := app.ListClusterNames() + if err != nil { + return "", err + } + if len(clusterNames) == 0 { + return "", fmt.Errorf("no Avalanche nodes found that can track the blockchain, please create Avalanche nodes first through `avalanche node create`") + } + clusterName, err := app.Prompt.CaptureList( + "Which cluster of Avalanche nodes would you like to use to track the blockchain?", + clusterNames, + ) + if err != nil { + return "", err + } + return clusterName, nil +} diff --git a/pkg/node/sync.go b/pkg/node/sync.go index 930b0a352..d30f1c38f 100644 --- a/pkg/node/sync.go +++ b/pkg/node/sync.go @@ -51,7 +51,7 @@ func SyncSubnet(app *application.Avalanche, clusterName, blockchainName string, if err := trackSubnet(app, hosts, clusterName, clusterConfig.Network, blockchainName, subnetAliases); err != nil { return err } - ux.Logger.PrintToUser("Node(s) successfully started syncing with Blockchain!") + ux.Logger.PrintToUser("Node(s) successfully started syncing with blockchain!") ux.Logger.PrintToUser(fmt.Sprintf("Check node blockchain syncing status with avalanche node status %s --blockchain %s", clusterName, blockchainName)) return nil }