Files
zeekayandHanzo Dev 15d4ed2eb5 A validator syncs the whole primary network
The CLI set --partial-sync-primary-network on every validator it brought
up, and defaulted the flag to true. luxd no longer has the mode: the
primary network's chains are one validator set, and a node that syncs
only the P-Chain casts no vote on the rest. The flag, the template line
that emitted it, and the remote-config round trip that read it back are
gone.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-03 13:46:48 -07:00

180 lines
6.6 KiB
Go

// Copyright (C) 2022-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package docker
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/luxfi/cli/pkg/remoteconfig"
"github.com/luxfi/cli/pkg/utils"
"github.com/luxfi/cli/pkg/ux"
"github.com/luxfi/constants"
"github.com/luxfi/sdk/models"
)
// ValidateComposeFile validates a docker-compose file on a remote host.
func ValidateComposeFile(host *models.Host, composeFile string, timeout time.Duration) error {
if output, err := host.Command(fmt.Sprintf("docker compose -f %s config", composeFile), nil, timeout); err != nil {
return fmt.Errorf("%w: %s", err, string(output))
}
return nil
}
// ComposeSSHSetupNode sets up an Luxd node and dependencies on a remote host over SSH.
func ComposeSSHSetupNode(
host *models.Host,
network models.Network,
luxdVersion string,
luxdBootstrapIDs []string,
luxdBootstrapIPs []string,
luxdGenesisFilePath string,
luxdUpgradeFilePath string,
withMonitoring bool,
publicAccessToHTTPPort bool,
) error {
startTime := time.Now()
folderStructure := remoteconfig.RemoteFoldersToCreateLuxgo()
for _, dir := range folderStructure {
if err := host.MkdirAll(dir, constants.SSHFileOpsTimeout); err != nil {
return fmt.Errorf("failed to create directory %s: %w", dir, err)
}
}
ux.Logger.Info("luxCLI folder structure created on remote host %s after %s ", folderStructure, time.Since(startTime))
luxdDockerImage := fmt.Sprintf("%s:%s", constants.LuxdDockerImage, luxdVersion)
ux.Logger.Info("Preparing Luxd Docker image %s on %s[%s]", luxdDockerImage, host.NodeID, host.IP)
if err := PrepareDockerImageWithRepo(host, luxdDockerImage, constants.LuxdGitRepo, luxdVersion); err != nil {
return err
}
ux.Logger.Info("Luxd Docker image %s ready on %s[%s] after %s", luxdDockerImage, host.NodeID, host.IP, time.Since(startTime))
nodeConfFile, cChainConfFile, err := prepareLuxgoConfig(
host,
network,
LuxdConfigOptions{
BootstrapIDs: luxdBootstrapIDs,
BootstrapIPs: luxdBootstrapIPs,
GenesisPath: luxdGenesisFilePath,
UpgradePath: luxdUpgradeFilePath,
AllowPublicAccess: publicAccessToHTTPPort,
},
)
if err != nil {
return err
}
defer func() {
if err := os.Remove(nodeConfFile); err != nil {
ux.Logger.Error("Error removing temporary file %s: %s", nodeConfFile, err)
}
if err := os.Remove(cChainConfFile); err != nil {
ux.Logger.Error("Error removing temporary file %s: %s", cChainConfFile, err)
}
}()
if err := host.Upload(nodeConfFile, remoteconfig.GetRemoteLuxNodeConfig(), constants.SSHFileOpsTimeout); err != nil {
return err
}
if err := host.Upload(cChainConfFile, remoteconfig.GetRemoteLuxCChainConfig(), constants.SSHFileOpsTimeout); err != nil {
return err
}
if luxdGenesisFilePath != "" {
if err := host.Upload(luxdGenesisFilePath, remoteconfig.GetRemoteLuxGenesis(), constants.SSHFileOpsTimeout); err != nil {
return err
}
}
if luxdUpgradeFilePath != "" {
if err := host.Upload(luxdUpgradeFilePath, remoteconfig.GetRemoteLuxUpgrade(), constants.SSHFileOpsTimeout); err != nil {
return err
}
}
ux.Logger.Info("Luxd configs uploaded to %s[%s] after %s", host.NodeID, host.IP, time.Since(startTime))
return ComposeOverSSH("Compose Node",
host,
constants.SSHScriptTimeout,
"templates/luxd.docker-compose.yml",
ComposeInputs{
LuxgoVersion: luxdVersion,
WithMonitoring: withMonitoring,
WithLuxgo: true,
E2E: utils.IsE2E(),
E2EIP: utils.E2EConvertIP(host.IP),
E2ESuffix: utils.E2ESuffix(host.IP),
})
}
// ComposeSSHSetupLoadTest sets up load test environment using docker-compose.
func ComposeSSHSetupLoadTest(host *models.Host) error {
return ComposeOverSSH("Compose Node",
host,
constants.SSHScriptTimeout,
"templates/luxd.docker-compose.yml",
ComposeInputs{
WithMonitoring: true,
WithLuxgo: false,
})
}
// WasNodeSetupWithMonitoring checks if an Luxd node was setup with monitoring on a remote host.
func WasNodeSetupWithMonitoring(host *models.Host) (bool, error) {
return HasRemoteComposeService(host, utils.GetRemoteComposeFile(), "promtail", constants.SSHScriptTimeout)
}
// ComposeSSHSetupMonitoring sets up monitoring using docker-compose.
func ComposeSSHSetupMonitoring(host *models.Host) error {
grafanaConfigFile, grafanaDashboardsFile, grafanaLokiDatasourceFile, grafanaPromDatasourceFile, err := prepareGrafanaConfig()
if err != nil {
return err
}
defer func() {
if err := os.Remove(grafanaLokiDatasourceFile); err != nil {
ux.Logger.Error("Error removing temporary file %s: %s", grafanaLokiDatasourceFile, err)
}
if err := os.Remove(grafanaPromDatasourceFile); err != nil {
ux.Logger.Error("Error removing temporary file %s: %s", grafanaPromDatasourceFile, err)
}
if err := os.Remove(grafanaDashboardsFile); err != nil {
ux.Logger.Error("Error removing temporary file %s: %s", grafanaDashboardsFile, err)
}
if err := os.Remove(grafanaConfigFile); err != nil {
ux.Logger.Error("Error removing temporary file %s: %s", grafanaConfigFile, err)
}
}()
grafanaLokiDatasourceRemoteFileName := filepath.Join(utils.GetRemoteComposeServicePath("grafana", "provisioning", "datasources"), "loki.yml")
if err := host.Upload(grafanaLokiDatasourceFile, grafanaLokiDatasourceRemoteFileName, constants.SSHFileOpsTimeout); err != nil {
return err
}
grafanaPromDatasourceFileName := filepath.Join(utils.GetRemoteComposeServicePath("grafana", "provisioning", "datasources"), "prometheus.yml")
if err := host.Upload(grafanaPromDatasourceFile, grafanaPromDatasourceFileName, constants.SSHFileOpsTimeout); err != nil {
return err
}
grafanaDashboardsRemoteFileName := filepath.Join(utils.GetRemoteComposeServicePath("grafana", "provisioning", "dashboards"), "dashboards.yml")
if err := host.Upload(grafanaDashboardsFile, grafanaDashboardsRemoteFileName, constants.SSHFileOpsTimeout); err != nil {
return err
}
grafanaConfigRemoteFileName := filepath.Join(utils.GetRemoteComposeServicePath("grafana"), "grafana.ini")
if err := host.Upload(grafanaConfigFile, grafanaConfigRemoteFileName, constants.SSHFileOpsTimeout); err != nil {
return err
}
return ComposeOverSSH("Setup Monitoring",
host,
constants.SSHScriptTimeout,
"templates/monitoring.docker-compose.yml",
ComposeInputs{})
}
// ComposeSSHSetupWarpRelayer sets up the AWM warp relayer using docker-compose.
func ComposeSSHSetupWarpRelayer(host *models.Host, relayerVersion string) error {
return ComposeOverSSH("Setup AWM Relayer",
host,
constants.SSHScriptTimeout,
"templates/awmrelayer.docker-compose.yml",
ComposeInputs{
WarpRelayerVersion: relayerVersion,
})
}