wip: 重新整理 install cmd

This commit is contained in:
zhaoyupeng
2026-01-13 20:13:29 +08:00
parent fcbaa5be2f
commit 760784a5ac
15 changed files with 957 additions and 665 deletions

View File

@@ -1,11 +1,18 @@
package installer
import (
"bufio"
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"yizhisec.com/hsv2/forge/pkg/logger"
"yizhisec.com/hsv2/forge/pkg/syscheck"
"yizhisec.com/hsv2/forge/pkg/tool/human"
)
type CheckOption func(*checkOpt)
@@ -13,6 +20,7 @@ type checkOpt struct {
ignoreDisk bool
ignoreMemory bool
ignoreCPU bool
noWriteDown bool
}
func WithIgnoreDiskCheck(ignore bool) CheckOption {
@@ -33,9 +41,177 @@ func WithIgnoreCPUCheck(ignore bool) CheckOption {
}
}
func (i *installer) Check(ctx context.Context, opts ...CheckOption) error {
func WithNoWriteDown(noWriteDown bool) CheckOption {
return func(o *checkOpt) {
o.noWriteDown = noWriteDown
}
}
type HardwareCheckResult struct {
Timestamp int64 // ms
DiskSize int64 // bytes, if err, -1; if ignore, 0
DiskReadSpeed int64 // bytes/s, if err, -1; if ignore, 0
DiskWriteSpeed int64 // bytes/s, if err, -1; if ignore, 0
MemorySize int64 // bytes, if err, -1; if ignore, 0
MemoryReadSpeed int64 // bytes/s, if err, -1; if ignore, 0
MemoryWriteSpeed int64 // bytes/s, if err, -1; if ignore, 0
CPUCores int64 // if err, -1; if ignore, 0
CPUFrequency int64 // MHz, if err, -1; if ignore, 0
CPUSupportAES bool
CPUIsX86V2 bool
}
func (h *HardwareCheckResult) Write(filename string) error {
content := []byte(fmt.Sprintf(`timestamp=%d
disk_size=%d
disk_read_speed=%d
disk_write_speed=%d
memory_size=%d
memory_read_speed=%d
memory_write_speed=%d
cpu_cores=%d
cpu_frequency=%d
cpu_support_aes=%t
cpu_is_x86_v2=%t`,
h.Timestamp,
h.DiskSize,
h.DiskReadSpeed,
h.DiskWriteSpeed,
h.MemorySize,
h.MemoryReadSpeed,
h.MemoryWriteSpeed,
h.CPUCores,
h.CPUFrequency,
h.CPUSupportAES,
h.CPUIsX86V2,
))
return os.WriteFile(filename, content, 0644)
}
func (h *HardwareCheckResult) Load(filename string) error {
file, err := os.Open(filename)
if err != nil {
return fmt.Errorf("failed to open file: %w", err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
continue
}
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
switch key {
case "timestamp":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
h.Timestamp = -1
} else {
h.Timestamp = v
}
case "disk_size":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
h.DiskSize = -1
} else {
h.DiskSize = v
}
case "disk_read_speed":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
h.DiskReadSpeed = -1
} else {
h.DiskReadSpeed = v
}
case "disk_write_speed":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
h.DiskWriteSpeed = -1
} else {
h.DiskWriteSpeed = v
}
case "memory_size":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
h.MemorySize = -1
} else {
h.MemorySize = v
}
case "memory_read_speed":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
h.MemoryReadSpeed = -1
} else {
h.MemoryReadSpeed = v
}
case "memory_write_speed":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
h.MemoryWriteSpeed = -1
} else {
h.MemoryWriteSpeed = v
}
case "cpu_cores":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
h.CPUCores = -1
} else {
h.CPUCores = v
}
case "cpu_frequency":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
h.CPUFrequency = -1
} else {
h.CPUFrequency = v
}
case "cpu_support_aes":
v, err := strconv.ParseBool(value)
if err == nil {
h.CPUSupportAES = v
}
// bool fields don't use -1, keep default false on error
case "cpu_is_x86_v2":
v, err := strconv.ParseBool(value)
if err == nil {
h.CPUIsX86V2 = v
}
// bool fields don't use -1, keep default false on error
}
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("failed to read file: %w", err)
}
return nil
}
func (h *HardwareCheckResult) Pass(opts ...CheckOption) error {
const (
DISK_SIZE int64 = 480 * 1024 * 1024 * 1024 // 480GB
DISK_READ_SPEED int64 = 1024 * 1024 * 1024 // 1024MB/s
DISK_WRITE_SPEED int64 = 500 * 1024 * 1024 // 500MB/s
MEMORY_SIZE int64 = 15.5 * 1024 * 1024 * 1024 // 15.5GB
MEMORY_READ_SPEED int64 = 1024 * 1024 * 1024 // 1024MB/s
MEMORY_WRITE_SPEED int64 = 500 * 1024 * 1024 // 500MB/s
CPUCORES int64 = 8
CPU_FREQUENCY int64 = 2.0 * 1024 // 2.0GHz
CPU_NEED_V2 = true
CPU_NEED_AES = true
)
var (
err error
now = time.Now()
o = &checkOpt{}
)
@@ -43,98 +219,195 @@ func (i *installer) Check(ctx context.Context, opts ...CheckOption) error {
fn(o)
}
logger.Info("☑️ installer.Check: Starting system checks...")
if err = i.targetOK(ctx); err != nil {
return err
if h.Timestamp < now.AddDate(0, 0, -1).UnixMilli() {
return fmt.Errorf("上次检测结果已失效, 请重新检测")
}
// 1. Check disk space: >= 500GB
logger.Info("☑️ installer.Check: Checking disk space...")
diskSpaceResult, err := syscheck.CheckDiskSpace(ctx, i, 500)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check disk space: %v", err)
return fmt.Errorf("failed to check disk space: %w", err)
if o.ignoreDisk || h.DiskSize+h.DiskReadSpeed+h.DiskWriteSpeed == -3 {
goto PASS_MEMORY
}
if !diskSpaceResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", diskSpaceResult.Name, diskSpaceResult.Message, diskSpaceResult.Expected, diskSpaceResult.Actual)
if !o.ignoreDisk {
return fmt.Errorf("disk space check failed: %s", diskSpaceResult.Message)
}
if h.DiskSize < DISK_SIZE {
return fmt.Errorf("磁盘大小不满足, 需要: %s, 实际: %s", human.Size(DISK_SIZE), human.Size(h.DiskSize))
}
logger.Info("✅ %s: %s", diskSpaceResult.Name, diskSpaceResult.Actual)
// 2. Check disk performance: write >= 500MB/s, read >= 500MB/s
logger.Info("☑️ installer.Check: Checking disk performance...")
diskPerfResult, err := syscheck.CheckDiskPerformance(ctx, i, 500, 500)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check disk performance: %v", err)
return fmt.Errorf("failed to check disk performance: %w", err)
if h.DiskReadSpeed < DISK_READ_SPEED {
return fmt.Errorf("磁盘读速度不满足, 需要: %s, 实际: %s", human.Size(DISK_READ_SPEED), human.Size(h.DiskReadSpeed))
}
if !diskPerfResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", diskPerfResult.Name, diskPerfResult.Message, diskPerfResult.Expected, diskPerfResult.Actual)
if !o.ignoreDisk {
return fmt.Errorf("disk performance check failed: %s", diskPerfResult.Message)
}
if h.DiskWriteSpeed < DISK_WRITE_SPEED {
return fmt.Errorf("磁盘写速度不满足, 需要: %s, 实际: %s", human.Size(DISK_WRITE_SPEED), human.Size(h.DiskWriteSpeed))
}
logger.Info("✅ %s: %s", diskPerfResult.Name, diskPerfResult.Actual)
PASS_MEMORY:
// 3. Check memory size: >= 15.5GB
logger.Info("☑️ installer.Check: Checking memory size...")
memResult, err := syscheck.CheckMemory(ctx, i, 15.5)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check memory: %v", err)
return fmt.Errorf("failed to check memory: %w", err)
if o.ignoreMemory || h.MemorySize+h.MemoryReadSpeed+h.MemoryWriteSpeed == -3 {
goto PASS_CPU
}
if !memResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", memResult.Name, memResult.Message, memResult.Expected, memResult.Actual)
if !o.ignoreMemory {
return fmt.Errorf("memory check failed: %s", memResult.Message)
}
if h.MemorySize < MEMORY_SIZE {
return fmt.Errorf("内存大小不满足, 需要: %s, 实际: %s", human.Size(MEMORY_SIZE), human.Size(h.MemorySize))
}
logger.Info("✅ %s: %s", memResult.Name, memResult.Actual)
// 4. Check CPU cores: >= 8
logger.Info("☑️ installer.Check: Checking CPU cores...")
cpuCoresResult, err := syscheck.CheckCPUCores(ctx, i, 8)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check CPU cores: %v", err)
return fmt.Errorf("failed to check CPU cores: %w", err)
if h.MemoryReadSpeed < MEMORY_READ_SPEED {
return fmt.Errorf("内存读速度不满足, 需要: %s, 实际: %s", human.Size(MEMORY_READ_SPEED), human.Size(h.MemoryReadSpeed))
}
if !cpuCoresResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", cpuCoresResult.Name, cpuCoresResult.Message, cpuCoresResult.Expected, cpuCoresResult.Actual)
if !o.ignoreCPU {
return fmt.Errorf("CPU cores check failed: %s", cpuCoresResult.Message)
}
if h.MemoryWriteSpeed < MEMORY_WRITE_SPEED {
return fmt.Errorf("内存写速度不满足, 需要: %s, 实际: %s", human.Size(MEMORY_WRITE_SPEED), human.Size(h.MemoryWriteSpeed))
}
logger.Info("✅ %s: %s", cpuCoresResult.Name, cpuCoresResult.Actual)
PASS_CPU:
// 5. Check CPU frequency: >= 2GHz
logger.Info("☑️ installer.Check: Checking CPU frequency...")
cpuFreqResult, err := syscheck.CheckCPUFrequency(ctx, i, 2.0)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check CPU frequency: %v", err)
return fmt.Errorf("failed to check CPU frequency: %w", err)
if o.ignoreCPU || h.CPUCores+h.CPUFrequency == -2 {
goto END
}
if h.CPUCores < CPUCORES {
return fmt.Errorf("cpu 核心数不满足, 需要: %d, 实际: %d", CPUCORES, h.CPUCores)
}
if !cpuFreqResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", cpuFreqResult.Name, cpuFreqResult.Message, cpuFreqResult.Expected, cpuFreqResult.Actual)
if !o.ignoreCPU {
return fmt.Errorf("CPU frequency check failed: %s", cpuFreqResult.Message)
}
if h.CPUFrequency < CPU_FREQUENCY {
return fmt.Errorf("cpu 频率不满足, 需要: %s, 实际: %s", human.Size(CPU_FREQUENCY), human.Size(h.CPUFrequency))
}
logger.Info("✅ %s: %s", cpuFreqResult.Name, cpuFreqResult.Actual)
if CPU_NEED_V2 && !h.CPUIsX86V2 {
return fmt.Errorf("cpu 不支持aes")
}
logger.Info("✅ installer.Check: All system checks passed successfully!")
if CPU_NEED_AES && !h.CPUSupportAES {
return fmt.Errorf("cpu 不支持 x86_v2s")
}
END:
return nil
}
func (i *installer) HardwareCheck(ctx context.Context, opts ...CheckOption) error {
var (
err error
o = &checkOpt{}
now = time.Now()
result = &HardwareCheckResult{
Timestamp: now.UnixMilli(),
}
cpuInfo syscheck.CPUInfo
dir string
)
for _, fn := range opts {
fn(o)
}
logger.Info("✅ 开始目标机器系统检测...")
if o.ignoreDisk {
logger.Warn("⚠️ 跳过磁盘检测")
result.DiskSize = -1
result.DiskReadSpeed = -1
result.DiskWriteSpeed = -1
goto CHECK_MEMORY
}
if result.DiskSize, err = syscheck.GetDiskSpace(ctx); err != nil {
logger.Debug("❌ installer.HardwareCheck: Failed to get disk space: %v", err)
return err
}
logger.Info("💾 磁盘容量: %s", human.Size(result.DiskSize))
if result.DiskReadSpeed, result.DiskWriteSpeed, err = syscheck.GetDiskSpeed(ctx); err != nil {
logger.Debug("❌ installer.HardwareCheck: Failed to get disk speed: %v", err)
return err
}
logger.Info("💾 磁盘速率: 读取(%s/每秒), 写入(%s/每秒)", human.Size(result.DiskReadSpeed), human.Size(result.DiskWriteSpeed))
CHECK_MEMORY:
if o.ignoreMemory {
logger.Warn("⚠️ 跳过内存检测")
result.MemorySize = -1
result.MemoryReadSpeed = -1
result.MemoryWriteSpeed = -1
goto CHECK_CPU
}
if result.MemorySize, err = syscheck.GetMemorySpace(ctx); err != nil {
logger.Debug("❌ installer.HardwareCheck: Failed to get memory size: %v", err)
return err
}
logger.Info("💿 内存容量: %s", human.Size(result.MemorySize))
if result.MemoryReadSpeed, result.MemoryWriteSpeed, err = syscheck.GetMemorySpeed(ctx); err != nil {
logger.Debug("❌ installer.HardwareCheck: Failed to get memory speed: %v", err)
return err
}
logger.Info("💿 内存速率: 读取(%s/每秒), 写入(%s/每秒)", human.Size(result.MemoryReadSpeed), human.Size(result.MemoryWriteSpeed))
CHECK_CPU:
if o.ignoreCPU {
logger.Warn("⚠️ 跳过 CPU 检测")
result.CPUCores = -1
result.CPUFrequency = -1
result.CPUSupportAES = false
result.CPUIsX86V2 = false
goto END
}
if cpuInfo, err = syscheck.GetCPUInfo(ctx); err != nil {
logger.Debug("❌ installer.HardwareCheck: Failed to get CPU info: %v", err)
return err
}
result.CPUCores = cpuInfo.Cores
result.CPUFrequency = cpuInfo.FrequencyMHz
result.CPUSupportAES = cpuInfo.SupportAES
result.CPUIsX86V2 = cpuInfo.IsX86V2
logger.Info("🧮 CPU 核心数: %d", result.CPUCores)
logger.Info("🧮 CPU 频率: %d Mhz", result.CPUFrequency)
logger.Info("🧮 CPU 支持 AES: %t", result.CPUSupportAES)
END:
if dir, err = os.UserHomeDir(); err != nil {
logger.Debug("❌ installer.HardwareCheck: Failed to get user home directory: %v", err)
return err
}
if err = result.Write(filepath.Join(dir, ".hsv2-installation")); err != nil {
logger.Debug("❌ installer.HardwareCheck: Failed to write installation file: %v", err)
return err
}
if err = result.Pass(opts...); err != nil {
logger.Error("❌ %s", err.Error())
return nil
}
logger.Info("✅ 检测完成")
return nil
}
func (*installer) CheckOK(ctx context.Context) error {
var (
err error
h = &HardwareCheckResult{}
dir string
)
if dir, err = os.UserHomeDir(); err != nil {
logger.Debug("❌ installer.HardwareCheck: Failed to get user home directory: %v", err)
return err
}
if err = h.Load(filepath.Join(dir, ".hsv2-installation")); err != nil {
logger.Debug("❌ installer.HardwareCheck: Failed to load installation file: %v", err)
return err
}
return h.Pass()
}

View File

@@ -2,41 +2,27 @@ package installer
import (
"context"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"yizhisec.com/hsv2/forge/pkg/logger"
"github.com/samber/lo"
)
type installer struct {
workdir string
target string
}
func (i *installer) buildCommand(ctx context.Context, cmds ...string) *exec.Cmd {
if len(cmds) == 0 {
return nil
}
if i.target == "self" {
return exec.CommandContext(ctx, cmds[0], cmds[1:]...)
}
sshArgs := append([]string{i.target}, cmds...)
return exec.CommandContext(ctx, "ssh", sshArgs...)
}
// ExecuteCommand implements syscheck.CommandExecutor interface
func (i *installer) ExecuteCommand(ctx context.Context, cmds ...string) (string, error) {
cmd := i.buildCommand(ctx, cmds...)
if cmd == nil {
return "", fmt.Errorf("failed to build command")
fcs := lo.Filter(cmds, func(item string, _ int) bool { return item != "" })
if len(fcs) == 0 {
return "", fmt.Errorf("empty commands")
}
cmd := exec.CommandContext(ctx, "sh", "-c", strings.Join(fcs, " "))
output, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("command failed: %w, output: %s", err, string(output))
@@ -45,109 +31,6 @@ func (i *installer) ExecuteCommand(ctx context.Context, cmds ...string) (string,
return string(output), nil
}
type CopyFileOption func(*copyFileOptions)
type copyFileOptions struct {
addExecutable bool
}
func withCopyFileExecutable() func(*copyFileOptions) {
return func(o *copyFileOptions) {
o.addExecutable = true
}
}
func (i *installer) copyFile(ctx context.Context, src, dst string, opts ...CopyFileOption) error {
logger.Debug("☑️ installer.copyFile: Copying file from %s to %s (target: %s)", src, dst, i.target)
var (
err error
o = &copyFileOptions{}
srcFile, dstFile *os.File
srcInfo os.FileInfo
)
for _, fn := range opts {
fn(o)
}
if i.target == "self" {
// Simply copy file locally
logger.Debug("Copying file locally: %s -> %s", src, dst)
// Open source file
if srcFile, err = os.Open(src); err != nil {
logger.Error("❌ Failed to open source file %s: %v", src, err)
return fmt.Errorf("failed to open source file: %w", err)
}
defer srcFile.Close()
// Create destination directory if needed
dstDir := filepath.Dir(dst)
if err = os.MkdirAll(dstDir, 0755); err != nil {
logger.Error("❌ Failed to create destination directory %s: %v", dstDir, err)
return fmt.Errorf("failed to create destination directory: %w", err)
}
// Create destination file
if dstFile, err = os.Create(dst); err != nil {
logger.Error("❌ Failed to create destination file %s: %v", dst, err)
return fmt.Errorf("failed to create destination file: %w", err)
}
defer dstFile.Close()
// Copy file content
if _, err = io.Copy(dstFile, srcFile); err != nil {
logger.Error("❌ Failed to copy file content: %v", err)
return fmt.Errorf("failed to copy file content: %w", err)
}
// Get source file permissions
if srcInfo, err = os.Stat(src); err == nil {
if err = os.Chmod(dst, srcInfo.Mode()); err != nil {
logger.Debug("⚠️ Failed to set file permissions: %v", err)
}
}
logger.Info("✅ File copied locally: %s -> %s", src, dst)
return nil
}
// Copy file via scp to remote target
logger.Debug("Copying file via scp: %s -> %s:%s", src, i.target, dst)
// Format: scp <src> <target>:<dst>
cmd := exec.CommandContext(ctx, "scp", src, fmt.Sprintf("%s:%s", i.target, dst))
output, err := cmd.CombinedOutput()
if err != nil {
logger.Error("❌ Failed to copy file via scp: %v, output: %s", err, string(output))
return fmt.Errorf("failed to copy file via scp: %w, output: %s", err, string(output))
}
logger.Info("✅ File copied via scp: %s -> %s:%s", src, i.target, dst)
return nil
}
func (i *installer) targetOK(ctx context.Context) error {
cmd := i.buildCommand(ctx, "whoami")
output, err := cmd.CombinedOutput()
if err != nil {
logger.Debug("❌ installer.targetOK: check target %s failed, err = %v", i.target, err)
return err
}
if string(output) != "root\n" {
logger.Debug("❌ installer.targetOK: check target %s failed, output = %s", i.target, string(output))
return errors.New("target is not root user")
}
return nil
}
func NewInstaller(workdir, target string) *installer {
if target == "" {
logger.Warn("🎯 NewInstaller: target empty, set to default(self)")
target = "self"
}
return &installer{workdir: workdir, target: target}
func NewInstaller(workdir string) *installer {
return &installer{workdir: workdir}
}

View File

@@ -2,12 +2,8 @@ package installer
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/samber/lo"
"yizhisec.com/hsv2/forge/pkg/logger"
)
type K0sOpt func(*k0sOpt)
@@ -41,83 +37,5 @@ func WithK0sWorkerTokenFile(filename string) K0sOpt {
}
func (i *installer) K0s(ctx context.Context, opts ...K0sOpt) error {
var (
err error
o = &k0sOpt{
Type: "controller",
controllerAsWorker: false,
WorkerTokenFile: "/etc/k0s/worker.token",
}
)
if err = i.targetOK(ctx); err != nil {
return err
}
for _, fn := range opts {
fn(o)
}
binaries := []string{
"dependency/bin/k0s",
"dependency/bin/k9s", "dependency/bin/kubectl", "dependency/bin/helm"}
if err = i.checkFiles(binaries...); err != nil {
return err
}
// check image tar files:
images := []string{
"dependency/image/k0s.apiserver-network-proxy-agent.tar",
"dependency/image/k0s.cni-node.tar",
"dependency/image/k0s.coredns.tar",
"dependency/image/k0s.kube-proxy.tar",
"dependency/image/k0s.kube-router.tar",
"dependency/image/k0s.metrics-server.tar",
"dependency/image/k0s.pause.tar",
}
if err = i.checkFiles(images...); err != nil {
return err
}
// copy binaries to /usr/local/bin and add executable permissions
if err = i.copyFile(ctx, "dependency/bin/k0s", "/usr/local/bin/k0s", withCopyFileExecutable()); err != nil {
return err
}
if err = i.copyFile(ctx, "dependency/bin/k9s", "/usr/local/bin/k9s", withCopyFileExecutable()); err != nil {
return err
}
if err = i.copyFile(ctx, "dependency/bin/kubectl", "/usr/local/bin/kubectl", withCopyFileExecutable()); err != nil {
return err
}
if err = i.copyFile(ctx, "dependency/bin/helm", "/usr/local/bin/helm", withCopyFileExecutable()); err != nil {
return err
}
i.ExecuteCommand(ctx, "k0s", "")
return nil
}
// checkBinaryFiles checks if the required binary files exist in the dependency/bin directory
func (i *installer) checkFiles(fileBaseName ...string) error {
logger.Info("☑️ installer.checkFiles: Checking files in %s...", i.workdir)
for _, file := range fileBaseName {
filename := filepath.Join(i.workdir, file)
logger.Debug("Checking file: %s", filename)
if _, err := os.Stat(filename); os.IsNotExist(err) {
logger.Error("❌ File not found: %s", filename)
return fmt.Errorf("file not found: %s", filename)
} else if err != nil {
logger.Error("❌ Failed to check file %s: %v", filename, err)
return fmt.Errorf("failed to check file %s: %w", filename, err)
}
logger.Info("✅ File found: %s", file)
}
logger.Info("✅ installer.checkBinaryFiles: All binary files verified successfully!")
return nil
panic("plz impl")
}

View File

@@ -14,10 +14,6 @@ func (i *installer) Prepare(ctx context.Context) error {
logger.Info("☑️ installer.Prepare: Starting system preparation...")
if err = i.targetOK(ctx); err != nil {
return err
}
// 1. Set timezone to Asia/Shanghai
logger.Info("☑️ installer.Prepare: Setting timezone to Asia/Shanghai...")
if err = i.setTimezone(ctx); err != nil {
@@ -65,31 +61,23 @@ func (i *installer) Prepare(ctx context.Context) error {
// setTimezone sets the system timezone to Asia/Shanghai
func (i *installer) setTimezone(ctx context.Context) error {
var (
err error
output string
)
// Check if timezone file exists
cmd := i.buildCommand(ctx, "test", "-f", "/usr/share/zoneinfo/Asia/Shanghai")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("timezone file /usr/share/zoneinfo/Asia/Shanghai not found")
if output, err = i.ExecuteCommand(ctx, "test", "-f", "/usr/share/zoneinfo/Asia/Shanghai"); err != nil {
return fmt.Errorf("failed to set timezone, err =%s, raw = %s", err.Error(), output)
}
// Remove old localtime link/file
cmd = i.buildCommand(ctx, "rm", "-f", "/etc/localtime")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
logger.Debug("Failed to remove /etc/localtime: %v", err)
if output, err = i.ExecuteCommand(ctx, "rm", "-f", "/etc/localtime"); err != nil {
return fmt.Errorf("failed to set timezone, err =%s, raw = %s", err.Error(), output)
}
// Create symlink
cmd = i.buildCommand(ctx, "ln", "-s", "/usr/share/zoneinfo/Asia/Shanghai", "/etc/localtime")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to create symlink: %w", err)
if output, err = i.ExecuteCommand(ctx, "ln", "-s", "/usr/share/zoneinfo/Asia/Shanghai", "/etc/localtime"); err != nil {
return fmt.Errorf("failed to set timezone, err =%s, raw = %s", err.Error(), output)
}
return nil
@@ -97,22 +85,19 @@ func (i *installer) setTimezone(ctx context.Context) error {
// disableSwap disables all swap partitions and removes swap entries from /etc/fstab
func (i *installer) disableSwap(ctx context.Context) error {
var (
err error
output string
)
// Turn off all swap
cmd := i.buildCommand(ctx, "swapoff", "-a")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
logger.Debug("Failed to swapoff: %v (may be already off)", err)
if output, err = i.ExecuteCommand(ctx, "swapoff", "-a"); err != nil {
logger.Debug("Failed to swapoff: %v (may be already off), raw = %s", err, output)
}
// Comment out swap entries in /etc/fstab to make it persistent
cmd = i.buildCommand(ctx, "sed", "-i", "/swap/s/^/#/", "/etc/fstab")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
logger.Debug("Failed to comment swap in /etc/fstab: %v", err)
if output, err = i.ExecuteCommand(ctx, "sed", "-i", "/swap/s/^/#/", "/etc/fstab"); err != nil {
logger.Debug("Failed to comment swap in /etc/fstab: %v, raw = %s", err, output)
}
return nil
@@ -120,23 +105,21 @@ func (i *installer) disableSwap(ctx context.Context) error {
// loadKernelModule loads a kernel module and ensures it's loaded on boot
func (i *installer) loadKernelModule(ctx context.Context, moduleName string) error {
var (
err error
output string
)
// Load the module immediately
cmd := i.buildCommand(ctx, "modprobe", moduleName)
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to load module %s: %w", moduleName, err)
if output, err = i.ExecuteCommand(ctx, "modprobe", moduleName); err != nil {
return fmt.Errorf("failed to load module %s: %w, raw = %s", moduleName, err, output)
}
// Add to /etc/modules-load.d/ to load on boot
filePath := fmt.Sprintf("/etc/modules-load.d/%s.conf", moduleName)
cmd = i.buildCommand(ctx, "bash", "-c", fmt.Sprintf("echo '%s' > %s", moduleName, filePath))
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
logger.Debug("Failed to add module to modules-load.d: %v", err)
command := fmt.Sprintf("echo '%s' > %s", moduleName, filePath)
if output, err = i.ExecuteCommand(ctx, "bash", "-c", command); err != nil {
logger.Debug("Failed to add module to modules-load.d: %v, raw = %s", err, output)
}
return nil
@@ -144,6 +127,11 @@ func (i *installer) loadKernelModule(ctx context.Context, moduleName string) err
// applySysctlSettings applies required sysctl settings for Kubernetes
func (i *installer) applySysctlSettings(ctx context.Context) error {
var (
err error
output string
)
const sysctlConfig = `# Kubernetes required settings
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
@@ -163,21 +151,14 @@ net.ipv4.neigh.default.gc_thresh3 = 8192
`
// Write sysctl config file
cmd := i.buildCommand(ctx, "bash", "-c", fmt.Sprintf("cat > /etc/sysctl.d/99-kubernetes.conf << 'EOF'\n%sEOF", sysctlConfig))
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to write sysctl config: %w", err)
command := fmt.Sprintf("cat > /etc/sysctl.d/99-kubernetes.conf << 'EOF'\n%sEOF", sysctlConfig)
if output, err = i.ExecuteCommand(ctx, "bash", "-c", command); err != nil {
return fmt.Errorf("failed to write sysctl config: %w, raw = %s", err, output)
}
// Apply sysctl settings
cmd = i.buildCommand(ctx, "sysctl", "--system")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to apply sysctl settings: %w", err)
if output, err = i.ExecuteCommand(ctx, "sysctl", "--system"); err != nil {
return fmt.Errorf("failed to apply sysctl settings: %w, raw = %s", err, output)
}
return nil