mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
163 lines
5.9 KiB
Go
163 lines
5.9 KiB
Go
// Copyright (C) 2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
package nodecmd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/luxfi/cli/v2/pkg/ansible"
|
|
"github.com/luxfi/cli/v2/pkg/cobrautils"
|
|
"github.com/luxfi/cli/v2/pkg/constants"
|
|
"github.com/luxfi/cli/v2/pkg/models"
|
|
"github.com/luxfi/cli/v2/pkg/node"
|
|
"github.com/luxfi/cli/v2/pkg/utils"
|
|
"github.com/luxfi/cli/v2/pkg/ux"
|
|
sdkutils "github.com/luxfi/cli/v2/sdk/utils"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func newImportCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "import [clusterName]",
|
|
Short: "(ALPHA Warning) Import cluster configuration from a file",
|
|
Long: `(ALPHA Warning) This command is currently in experimental mode.
|
|
|
|
The node import command imports cluster configuration and its nodes configuration from a text file
|
|
created from the node export command.
|
|
|
|
Prior to calling this command, call node whitelist command to have your SSH public key and IP whitelisted by
|
|
the cluster owner. This will enable you to use lux-cli commands to manage the imported cluster.
|
|
|
|
Please note, that this imported cluster will be considered as EXTERNAL by lux-cli, so some commands
|
|
affecting cloud nodes like node create or node destroy will be not applicable to it.`,
|
|
Args: cobrautils.ExactArgs(1),
|
|
RunE: importFile,
|
|
}
|
|
cmd.Flags().StringVar(&clusterFileName, "file", "", "specify the file to export the cluster configuration to")
|
|
return cmd
|
|
}
|
|
|
|
func importFile(_ *cobra.Command, args []string) error {
|
|
clusterName := args[0]
|
|
if clusterExists, err := node.CheckClusterExists(app, clusterName); clusterExists || err != nil {
|
|
ux.Logger.RedXToUser("cluster %s already exists, please use a different name", clusterName)
|
|
return nil
|
|
}
|
|
|
|
importCluster, err := readExportClusterFromFile(clusterFileName)
|
|
if err != nil {
|
|
ux.Logger.RedXToUser("error reading file: %v", err)
|
|
return err
|
|
}
|
|
importCluster.ClusterConfig.External = true // mark cluster as external
|
|
// check for existing nodes
|
|
nodestoCheck := importCluster.Nodes
|
|
nodestoCheck = append(nodestoCheck, importCluster.LoadTestNodes...)
|
|
if importCluster.ClusterConfig.MonitoringInstance != "" {
|
|
nodestoCheck = append(nodestoCheck, importCluster.MonitorNode)
|
|
}
|
|
for _, node := range nodestoCheck {
|
|
keyPath := filepath.Join(app.GetNodesDir(), node.NodeConfig.NodeID)
|
|
if sdkutils.DirExists(keyPath) {
|
|
ux.Logger.RedXToUser("node %s already exists and belongs to the existing cluster, can't import", node.NodeConfig.NodeID)
|
|
ux.Logger.RedXToUser("you can use destroy command to remove the cluster it belongs to and then retry import")
|
|
return nil
|
|
}
|
|
}
|
|
// add nodes
|
|
for _, node := range importCluster.Nodes {
|
|
keyPath := filepath.Join(app.GetNodesDir(), node.NodeConfig.NodeID)
|
|
nc := node.NodeConfig
|
|
if err := app.CreateNodeCloudConfigFile(node.NodeConfig.NodeID, &nc); err != nil {
|
|
ux.Logger.RedXToUser("error creating node config file: %v", err)
|
|
return err
|
|
}
|
|
if err := writeSecretToFile(node.StakerKey, filepath.Join(keyPath, constants.StakerKeyFileName)); err != nil {
|
|
return err
|
|
}
|
|
if err := writeSecretToFile(node.StakerCrt, filepath.Join(keyPath, constants.StakerCertFileName)); err != nil {
|
|
return err
|
|
}
|
|
if err := writeSecretToFile(node.SignerKey, filepath.Join(keyPath, constants.BLSKeyFileName)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if importCluster.ClusterConfig.MonitoringInstance != "" {
|
|
if err := app.CreateNodeCloudConfigFile(importCluster.MonitorNode.NodeConfig.NodeID, &importCluster.MonitorNode.NodeConfig); err != nil {
|
|
ux.Logger.RedXToUser("error creating monitor node config file: %v", err)
|
|
return err
|
|
}
|
|
}
|
|
// add inventory
|
|
inventoryPath := app.GetAnsibleInventoryDirPath(clusterName)
|
|
nodes := sdkutils.Map(importCluster.Nodes, func(node models.ExportNode) models.NodeConfig { return node.NodeConfig })
|
|
if err := ansible.WriteNodeConfigsToAnsibleInventory(inventoryPath, nodes); err != nil {
|
|
ux.Logger.RedXToUser("error writing inventory file: %v", err)
|
|
return err
|
|
}
|
|
if importCluster.ClusterConfig.MonitoringInstance != "" {
|
|
monitoringInventoryPath := app.GetMonitoringInventoryDir(clusterName)
|
|
if err := ansible.WriteNodeConfigsToAnsibleInventory(monitoringInventoryPath, []models.NodeConfig{importCluster.MonitorNode.NodeConfig}); err != nil {
|
|
ux.Logger.RedXToUser("error writing monitoring inventory file: %v", err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
// add cluster
|
|
clustersConfig := models.ClustersConfig{}
|
|
clustersConfig.Clusters = make(map[string]models.ClusterConfig)
|
|
clustersConfig, err = app.GetClustersConfig()
|
|
if err != nil {
|
|
ux.Logger.RedXToUser("error loading clusters config: %v", err)
|
|
return err
|
|
}
|
|
|
|
importCluster.ClusterConfig.Network.ClusterName = clusterName
|
|
clustersConfig.Clusters[clusterName] = importCluster.ClusterConfig
|
|
if err := app.WriteClustersConfigFile(&clustersConfig); err != nil {
|
|
ux.Logger.RedXToUser("error saving clusters config: %v", err)
|
|
}
|
|
ux.Logger.GreenCheckmarkToUser("cluster [%s] imported successfully", clusterName)
|
|
return nil
|
|
}
|
|
|
|
// readExportClusterFromFile reads the export cluster configuration from a file
|
|
func readExportClusterFromFile(filename string) (models.ExportCluster, error) {
|
|
var cluster models.ExportCluster
|
|
if !utils.FileExists(utils.ExpandHome(filename)) {
|
|
return cluster, fmt.Errorf("file does not exist")
|
|
} else {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return cluster, err
|
|
}
|
|
defer file.Close()
|
|
data, err := io.ReadAll(file)
|
|
if err != nil {
|
|
return cluster, err
|
|
}
|
|
err = json.Unmarshal(data, &cluster)
|
|
if err != nil {
|
|
return cluster, err
|
|
}
|
|
return cluster, nil
|
|
}
|
|
}
|
|
|
|
// writeSecretToFile writes a secret to a file
|
|
func writeSecretToFile(secret, filePath string) error {
|
|
if secret == "" {
|
|
return nil // nothing to write(no error)
|
|
}
|
|
if err := utils.WriteStringToFile(filePath, secret); err != nil {
|
|
ux.Logger.RedXToUser("error writing %s file: %w", filePath, err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|