Files
waf-platform/EdgeAPI/internal/installers/installer_httpdns_node.go
2026-03-13 14:25:13 +08:00

232 lines
6.8 KiB
Go

package installers
import (
"bytes"
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
)
type HTTPDNSNodeInstaller struct {
BaseInstaller
}
func (i *HTTPDNSNodeInstaller) Install(dir string, params interface{}, installStatus *models.NodeInstallStatus) error {
if params == nil {
return errors.New("'params' required for node installation")
}
nodeParams, ok := params.(*NodeParams)
if !ok {
return errors.New("'params' should be *NodeParams")
}
err := nodeParams.Validate()
if err != nil {
return fmt.Errorf("params validation: %w", err)
}
installRootDir, appDir := resolveHTTPDNSInstallPaths(dir)
_, err = i.client.Stat(installRootDir)
if err != nil {
err = i.client.MkdirAll(installRootDir)
if err != nil {
installStatus.ErrorCode = "CREATE_ROOT_DIRECTORY_FAILED"
return fmt.Errorf("create directory '%s' failed: %w", installRootDir, err)
}
}
env, err := i.InstallHelper(installRootDir, nodeconfigs.NodeRoleHTTPDNS)
if err != nil {
installStatus.ErrorCode = "INSTALL_HELPER_FAILED"
return err
}
filePrefix := "edge-httpdns-" + env.OS + "-" + env.Arch
zipFile, err := i.LookupLatestInstallerForTarget(filePrefix, env)
if err != nil {
return err
}
if len(zipFile) == 0 {
return errors.New("can not find installer file for " + env.OS + "/" + env.Arch + ", expected '" + filePrefix + "-v*.zip' or distro-specific '" + filePrefix + "-{ubuntu22.04|amzn2023}-v*.zip'")
}
targetZip, err := i.copyZipToRemote(installRootDir, zipFile)
if err != nil {
return err
}
if !nodeParams.IsUpgrading {
_, stderr, testErr := i.client.Exec(env.HelperPath + " -cmd=test")
if testErr != nil {
return fmt.Errorf("test failed: %w", testErr)
}
if len(stderr) > 0 {
return errors.New("test failed: " + stderr)
}
}
exePath := appDir + "/bin/edge-httpdns"
if nodeParams.IsUpgrading {
_, err = i.client.Stat(exePath)
if err == nil {
_, _, _ = i.client.Exec(exePath + " stop")
removeErr := i.client.Remove(exePath)
if removeErr != nil && removeErr != os.ErrNotExist {
return fmt.Errorf("remove old file failed: %w", removeErr)
}
}
}
_, stderr, err := i.client.Exec(env.HelperPath + " -cmd=unzip -zip=\"" + targetZip + "\" -target=\"" + installRootDir + "\"")
if err != nil {
return err
}
if len(stderr) > 0 {
return errors.New("unzip installer failed: " + stderr)
}
certFile := appDir + "/configs/tls/server.crt"
keyFile := appDir + "/configs/tls/server.key"
err = i.writeTLSCertificate(certFile, keyFile, nodeParams.TLSCertData, nodeParams.TLSKeyData)
if err != nil {
installStatus.ErrorCode = "WRITE_TLS_CERT_FAILED"
return err
}
configFile := appDir + "/configs/api_httpdns.yaml"
if i.client.sudo {
_, _, _ = i.client.Exec("chown " + i.client.User() + " " + filepath.Dir(configFile))
}
listenAddr := strings.TrimSpace(nodeParams.HTTPDNSListenAddr)
if len(listenAddr) == 0 {
listenAddr = ":443"
}
configData := []byte(`rpc.endpoints: [ ${endpoints} ]
nodeId: "${nodeId}"
secret: "${nodeSecret}"
https.listenAddr: "${listenAddr}"
https.cert: "${certFile}"
https.key: "${keyFile}"`)
certFileClean := strings.ReplaceAll(certFile, "\\", "/")
keyFileClean := strings.ReplaceAll(keyFile, "\\", "/")
configData = bytes.ReplaceAll(configData, []byte("${endpoints}"), []byte(nodeParams.QuoteEndpoints()))
configData = bytes.ReplaceAll(configData, []byte("${nodeId}"), []byte(nodeParams.NodeId))
configData = bytes.ReplaceAll(configData, []byte("${nodeSecret}"), []byte(nodeParams.Secret))
configData = bytes.ReplaceAll(configData, []byte("${listenAddr}"), []byte(listenAddr))
configData = bytes.ReplaceAll(configData, []byte("${certFile}"), []byte(certFileClean))
configData = bytes.ReplaceAll(configData, []byte("${keyFile}"), []byte(keyFileClean))
_, err = i.client.WriteFile(configFile, configData)
if err != nil {
return fmt.Errorf("write '%s': %w", configFile, err)
}
startCmdPrefix := "cd " + shQuote(appDir+"/configs") + " && ../bin/edge-httpdns "
stdout, stderr, err := i.client.Exec(startCmdPrefix + "test")
if err != nil {
installStatus.ErrorCode = "TEST_FAILED"
return fmt.Errorf("test edge-httpdns failed: %w, stdout: %s, stderr: %s", err, stdout, stderr)
}
if len(stderr) > 0 {
if regexp.MustCompile(`(?i)rpc`).MatchString(stderr) || regexp.MustCompile(`(?i)rpc`).MatchString(stdout) {
installStatus.ErrorCode = "RPC_TEST_FAILED"
}
return errors.New("test edge-httpdns failed, stdout: " + stdout + ", stderr: " + stderr)
}
stdout, stderr, err = i.client.Exec(startCmdPrefix + "start")
if err != nil {
return fmt.Errorf("start edge-httpdns failed: %w, stdout: %s, stderr: %s", err, stdout, stderr)
}
if len(stderr) > 0 {
return errors.New("start edge-httpdns failed, stdout: " + stdout + ", stderr: " + stderr)
}
return nil
}
func resolveHTTPDNSInstallPaths(rawDir string) (installRootDir string, appDir string) {
dir := strings.TrimSpace(rawDir)
dir = strings.TrimRight(dir, "/")
if len(dir) == 0 {
return rawDir, rawDir + "/edge-httpdns"
}
if strings.HasSuffix(dir, "/edge-httpdns") {
root := strings.TrimSuffix(dir, "/edge-httpdns")
if len(root) == 0 {
root = "/"
}
return root, dir
}
return dir, dir + "/edge-httpdns"
}
func (i *HTTPDNSNodeInstaller) copyZipToRemote(dir string, zipFile string) (string, error) {
targetZip := ""
var firstCopyErr error
zipName := filepath.Base(zipFile)
for _, candidate := range []string{
dir + "/" + zipName,
i.client.UserHome() + "/" + zipName,
"/tmp/" + zipName,
} {
err := i.client.Copy(zipFile, candidate, 0777)
if err != nil {
if firstCopyErr == nil {
firstCopyErr = err
}
continue
}
targetZip = candidate
firstCopyErr = nil
break
}
if firstCopyErr != nil {
return "", fmt.Errorf("upload httpdns file failed: %w", firstCopyErr)
}
return targetZip, nil
}
func (i *HTTPDNSNodeInstaller) writeTLSCertificate(certFile string, keyFile string, certData []byte, keyData []byte) error {
if len(certData) == 0 || len(keyData) == 0 {
return errors.New("cluster tls certificate is empty")
}
certDir := filepath.Dir(certFile)
_, stderr, err := i.client.Exec("mkdir -p " + shQuote(certDir))
if err != nil {
return fmt.Errorf("create tls directory failed: %w, stderr: %s", err, stderr)
}
if i.client.sudo {
_, _, _ = i.client.Exec("chown " + i.client.User() + " " + shQuote(certDir))
}
_, err = i.client.WriteFile(certFile, certData)
if err != nil {
return fmt.Errorf("write cert file failed: %w", err)
}
_, err = i.client.WriteFile(keyFile, keyData)
if err != nil {
return fmt.Errorf("write key file failed: %w", err)
}
_, stderr, err = i.client.Exec("chmod 0644 " + shQuote(certFile) + " && chmod 0600 " + shQuote(keyFile))
if err != nil {
return fmt.Errorf("chmod tls files failed: %w, stderr: %s", err, stderr)
}
return nil
}