mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
248 lines
11 KiB
Go
248 lines
11 KiB
Go
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package root
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
"regexp"
|
|
"time"
|
|
|
|
"github.com/luxfi/cli/v2/pkg/constants"
|
|
"github.com/luxfi/cli/v2/pkg/models"
|
|
"github.com/luxfi/cli/v2/pkg/utils"
|
|
"github.com/luxfi/cli/v2/tests/e2e/commands"
|
|
e2eUtils "github.com/luxfi/cli/v2/tests/e2e/utils"
|
|
ginkgo "github.com/onsi/ginkgo/v2"
|
|
"github.com/onsi/gomega"
|
|
)
|
|
|
|
const (
|
|
luxdVersion = "v1.13.0"
|
|
network = "testnet"
|
|
networkCapitalized = "Testnet"
|
|
numNodes = 1
|
|
exportFileName = "e2e.json"
|
|
importClusterName = "e2e_imported"
|
|
nodesRelativePath = "nodes"
|
|
)
|
|
|
|
var (
|
|
hostName string
|
|
NodeID string
|
|
ElasticIP string
|
|
)
|
|
|
|
var _ = ginkgo.Describe("[Node create]", func() {
|
|
ginkgo.It("can create a node", func() {
|
|
output := commands.NodeCreate(network, luxdVersion, numNodes, false, 0, commands.ExpectSuccess)
|
|
fmt.Println(output)
|
|
gomega.Expect(output).To(gomega.ContainSubstring("Luxd and Lux-CLI installed and node(s) are bootstrapping!"))
|
|
// parse hostName
|
|
re := regexp.MustCompile(`Generated staking keys for host (\S+)\[NodeID-(\S+)\]`)
|
|
match := re.FindStringSubmatch(output)
|
|
if len(match) >= 3 {
|
|
var err error
|
|
hostName, err = e2eUtils.GetE2EHostInstanceID()
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
NodeID = fmt.Sprintf("NodeID-%s", match[2])
|
|
} else {
|
|
ginkgo.Fail("failed to parse hostName and NodeID")
|
|
}
|
|
})
|
|
ginkgo.It("creates cluster config", func() {
|
|
usr, err := user.Current()
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
homeDir := usr.HomeDir
|
|
content, err := os.ReadFile(filepath.Join(homeDir, constants.E2EBaseDirName, nodesRelativePath, constants.ClustersConfigFileName))
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
clustersConfig := models.ClustersConfig{}
|
|
err = json.Unmarshal(content, &clustersConfig)
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
gomega.Expect(clustersConfig.Clusters).To(gomega.HaveLen(1))
|
|
gomega.Expect(clustersConfig.Clusters[constants.E2EClusterName].External).To(gomega.BeFalse())
|
|
gomega.Expect(clustersConfig.Clusters[constants.E2EClusterName].Network.Kind.String()).To(gomega.Equal(networkCapitalized))
|
|
gomega.Expect(clustersConfig.Clusters[constants.E2EClusterName].Nodes).To(gomega.HaveLen(numNodes))
|
|
})
|
|
ginkgo.It("creates node config", func() {
|
|
fmt.Println("HostName: ", hostName)
|
|
usr, err := user.Current()
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
homeDir := usr.HomeDir
|
|
content, err := os.ReadFile(filepath.Join(homeDir, constants.E2EBaseDirName, nodesRelativePath, hostName, "node_cloud_config.json"))
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
nodeCloudConfig := models.NodeConfig{}
|
|
err = json.Unmarshal(content, &nodeCloudConfig)
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
gomega.Expect(nodeCloudConfig.NodeID).To(gomega.Equal(hostName))
|
|
gomega.Expect(nodeCloudConfig.ElasticIP).To(gomega.ContainSubstring(constants.E2ENetworkPrefix))
|
|
gomega.Expect(nodeCloudConfig.CertPath).To(gomega.ContainSubstring(homeDir))
|
|
gomega.Expect(nodeCloudConfig.UseStaticIP).To(gomega.Equal(false))
|
|
ElasticIP = nodeCloudConfig.ElasticIP
|
|
})
|
|
ginkgo.It("installs and runs luxd", func() {
|
|
luxdProcess := commands.NodeSSH(constants.E2EClusterName, "docker ps --no-trunc")
|
|
gomega.Expect(luxdProcess).To(gomega.ContainSubstring("avaplatform/luxd:" + luxdVersion))
|
|
})
|
|
ginkgo.It("can wait up 30 seconds for luxd to startup", func() {
|
|
timeout := 30 * time.Second
|
|
address := fmt.Sprintf("%s:%d", utils.E2EConvertIP(ElasticIP), constants.LuxdP2PPort)
|
|
deadline := time.Now().Add(timeout)
|
|
var err error
|
|
|
|
for time.Now().Before(deadline) {
|
|
conn, err := net.DialTimeout("tcp", address, 1*time.Second)
|
|
if err == nil {
|
|
conn.Close()
|
|
break
|
|
}
|
|
time.Sleep(2 * time.Second)
|
|
}
|
|
gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Expected to connect to luxd within 30 seconds")
|
|
})
|
|
ginkgo.It("configured luxd", func() {
|
|
luxdConfig := commands.NodeSSH(constants.E2EClusterName, "cat /home/ubuntu/.luxd/configs/node.json")
|
|
gomega.Expect(luxdConfig).To(gomega.ContainSubstring("\"network-id\": \"" + network + "\""))
|
|
gomega.Expect(luxdConfig).To(gomega.ContainSubstring("public-ip"))
|
|
luxdConfigCChain := commands.NodeSSH(constants.E2EClusterName, "cat /home/ubuntu/.luxd/configs/chains/C/config.json")
|
|
gomega.Expect(luxdConfigCChain).To(gomega.ContainSubstring("\"state-sync-enabled\": true"))
|
|
})
|
|
ginkgo.It("can ssh to a created node", func() {
|
|
output := commands.NodeSSH(constants.E2EClusterName, "echo hello")
|
|
gomega.Expect(output).To(gomega.ContainSubstring("hello"))
|
|
})
|
|
ginkgo.It("can list created nodes", func() {
|
|
output := commands.NodeList()
|
|
fmt.Println(output)
|
|
gomega.Expect(output).To(gomega.ContainSubstring(networkCapitalized))
|
|
gomega.Expect(output).To(gomega.ContainSubstring("docker1"))
|
|
gomega.Expect(output).To(gomega.ContainSubstring("NodeID"))
|
|
gomega.Expect(output).To(gomega.ContainSubstring(constants.E2ENetworkPrefix))
|
|
})
|
|
ginkgo.It("logged operations", func() {
|
|
ls := commands.NodeSSH(constants.E2EClusterName, "ls -l /home/ubuntu/.luxd/*")
|
|
fmt.Println(ls)
|
|
gomega.Expect(ls).To(gomega.ContainSubstring("db"))
|
|
gomega.Expect(ls).To(gomega.ContainSubstring("logs"))
|
|
gomega.Expect(ls).To(gomega.ContainSubstring("configs"))
|
|
logs := commands.NodeSSH(constants.E2EClusterName, "cat /home/ubuntu/.luxd/logs/main.log")
|
|
gomega.Expect(logs).To(gomega.ContainSubstring("initializing node"))
|
|
gomega.Expect(logs).To(gomega.ContainSubstring("initializing API server"))
|
|
gomega.Expect(logs).To(gomega.ContainSubstring("creating leveldb"))
|
|
gomega.Expect(logs).To(gomega.ContainSubstring("initializing database"))
|
|
gomega.Expect(logs).To(gomega.ContainSubstring("creating proposervm wrapper"))
|
|
gomega.Expect(logs).To(gomega.ContainSubstring("check started passing"))
|
|
})
|
|
ginkgo.It("can get cluster status", func() {
|
|
output := commands.NodeStatus()
|
|
fmt.Println(output)
|
|
gomega.Expect(output).To(gomega.ContainSubstring("Checking node(s) status"))
|
|
gomega.Expect(output).To(gomega.ContainSubstring("Checking if node(s) are healthy"))
|
|
gomega.Expect(output).To(gomega.ContainSubstring("Getting luxd version of node(s)"))
|
|
gomega.Expect(output).To(gomega.ContainSubstring(constants.E2ENetworkPrefix))
|
|
gomega.Expect(output).To(gomega.ContainSubstring(hostName))
|
|
gomega.Expect(output).To(gomega.ContainSubstring(NodeID))
|
|
gomega.Expect(output).To(gomega.ContainSubstring(networkCapitalized))
|
|
})
|
|
ginkgo.It("can upgrade the nodes", func() {
|
|
output := commands.NodeUpgrade()
|
|
fmt.Println(output)
|
|
latestLuxdVersion := commands.GetLatestLuxdVersionFromGithub()
|
|
luxdVersion := commands.NodeSSH(constants.E2EClusterName, "docker ps --no-trunc")
|
|
gomega.Expect(luxdVersion).To(gomega.ContainSubstring("avaplatform/luxd:" + latestLuxdVersion))
|
|
})
|
|
ginkgo.It("can whitelist ssh", func() {
|
|
output := commands.NodeWhitelistSSH("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC test@localhost")
|
|
fmt.Println(output)
|
|
gomega.Expect(output).To(gomega.ContainSubstring("Whitelisted SSH public key"))
|
|
authorizedFile := commands.NodeSSH(constants.E2EClusterName, "cat /home/ubuntu/.ssh/authorized_keys")
|
|
gomega.Expect(authorizedFile).To(gomega.ContainSubstring("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC test@localhost"))
|
|
})
|
|
ginkgo.It("can export cluster config", func() {
|
|
const fileNameWithSecrets = "e2e_with_secrets.json"
|
|
output := commands.NodeExport(exportFileName, false)
|
|
fmt.Println(output)
|
|
gomega.Expect(output).To(gomega.ContainSubstring(fmt.Sprintf("exported cluster [%s] configuration", constants.E2EClusterName)))
|
|
gomega.Expect(output).To(gomega.ContainSubstring(fmt.Sprintf("to /home/runner/work/lux-cli/lux-cli/%s", exportFileName)))
|
|
exportFile, err := utils.ReadFile(exportFileName)
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
fmt.Println(exportFile)
|
|
content, err := os.ReadFile(exportFileName)
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
exportCluster := models.ExportCluster{}
|
|
err = json.Unmarshal(content, &exportCluster)
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
gomega.Expect(exportCluster.Nodes).To(gomega.HaveLen(1))
|
|
gomega.Expect(exportCluster.Nodes[0].NodeConfig.NodeID).To(gomega.Equal(hostName))
|
|
gomega.Expect(exportCluster.Nodes[0].NodeConfig.ElasticIP).To(gomega.ContainSubstring(constants.E2ENetworkPrefix))
|
|
gomega.Expect(exportCluster.Nodes[0].StakerCrt).To(gomega.Not(gomega.BeEmpty()))
|
|
gomega.Expect(exportCluster.Nodes[0].SignerKey).To(gomega.BeEmpty())
|
|
gomega.Expect(exportCluster.Nodes[0].StakerKey).To(gomega.BeEmpty())
|
|
gomega.Expect(exportCluster.ClusterConfig.External).To(gomega.BeTrue())
|
|
// export with secrets now
|
|
output = commands.NodeExport(fileNameWithSecrets, true)
|
|
fmt.Println(output)
|
|
content, err = os.ReadFile(fileNameWithSecrets)
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
exportCluster = models.ExportCluster{}
|
|
err = json.Unmarshal(content, &exportCluster)
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
gomega.Expect(exportCluster.Nodes).To(gomega.HaveLen(1))
|
|
gomega.Expect(exportCluster.Nodes[0].StakerCrt).To(gomega.Not(gomega.BeEmpty()))
|
|
gomega.Expect(exportCluster.Nodes[0].SignerKey).To(gomega.Not(gomega.BeEmpty()))
|
|
gomega.Expect(exportCluster.Nodes[0].StakerKey).To(gomega.Not(gomega.BeEmpty()))
|
|
})
|
|
ginkgo.It("can import cluster config", func() {
|
|
// prepare to reimport the same cluster in different name
|
|
commands.DeleteNode(hostName)
|
|
commands.DeleteE2EInventory()
|
|
// ready to import
|
|
output := commands.NodeImport(exportFileName, importClusterName)
|
|
fmt.Println(output)
|
|
gomega.Expect(output).To(gomega.ContainSubstring(fmt.Sprintf("cluster [%s] imported successfully", importClusterName)))
|
|
// check if the cluster is imported now
|
|
})
|
|
ginkgo.It("imported cluster config", func() {
|
|
usr, err := user.Current()
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
homeDir := usr.HomeDir
|
|
content, err := os.ReadFile(filepath.Join(homeDir, constants.E2EBaseDirName, nodesRelativePath, constants.ClustersConfigFileName))
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
clustersConfig := models.ClustersConfig{}
|
|
err = json.Unmarshal(content, &clustersConfig)
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
gomega.Expect(clustersConfig.Clusters).To(gomega.HaveLen(2))
|
|
gomega.Expect(clustersConfig.Clusters[importClusterName].External).To(gomega.BeTrue())
|
|
gomega.Expect(clustersConfig.Clusters[importClusterName].Network.Kind.String()).To(gomega.Equal(networkCapitalized))
|
|
gomega.Expect(clustersConfig.Clusters[importClusterName].Nodes).To(gomega.HaveLen(numNodes))
|
|
})
|
|
ginkgo.It("imported node config", func() {
|
|
fmt.Println("HostName: ", hostName)
|
|
usr, err := user.Current()
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
homeDir := usr.HomeDir
|
|
content, err := os.ReadFile(filepath.Join(homeDir, constants.E2EBaseDirName, nodesRelativePath, hostName, "node_cloud_config.json"))
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
nodeCloudConfig := models.NodeConfig{}
|
|
err = json.Unmarshal(content, &nodeCloudConfig)
|
|
gomega.Expect(err).Should(gomega.BeNil())
|
|
gomega.Expect(nodeCloudConfig.NodeID).To(gomega.Equal(hostName))
|
|
gomega.Expect(nodeCloudConfig.ElasticIP).To(gomega.ContainSubstring(constants.E2ENetworkPrefix))
|
|
gomega.Expect(nodeCloudConfig.CertPath).To(gomega.BeEmpty())
|
|
gomega.Expect(nodeCloudConfig.KeyPair).To(gomega.BeEmpty())
|
|
gomega.Expect(nodeCloudConfig.SecurityGroup).To(gomega.BeEmpty())
|
|
gomega.Expect(nodeCloudConfig.UseStaticIP).To(gomega.Equal(false))
|
|
})
|
|
ginkgo.It("can cleanup", func() {
|
|
commands.DeleteE2EInventory()
|
|
commands.DeleteE2ECluster()
|
|
commands.DeleteNode(hostName)
|
|
_ = os.Remove(exportFileName)
|
|
})
|
|
})
|