348 lines
9.4 KiB
Go
348 lines
9.4 KiB
Go
package storage
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/beclab/Olares/cli/pkg/core/logger"
|
|
|
|
"github.com/beclab/Olares/cli/pkg/common"
|
|
cc "github.com/beclab/Olares/cli/pkg/core/common"
|
|
"github.com/beclab/Olares/cli/pkg/core/connector"
|
|
"github.com/beclab/Olares/cli/pkg/core/task"
|
|
"github.com/beclab/Olares/cli/pkg/core/util"
|
|
"github.com/beclab/Olares/cli/pkg/manifest"
|
|
"github.com/beclab/Olares/cli/pkg/utils"
|
|
|
|
redisTemplates "github.com/beclab/Olares/cli/pkg/storage/templates"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type CheckRedisServiceState struct {
|
|
common.KubeAction
|
|
}
|
|
|
|
func (t *CheckRedisServiceState) Execute(runtime connector.Runtime) error {
|
|
var systemInfo = runtime.GetSystemInfo()
|
|
var localIp = systemInfo.GetLocalIp()
|
|
var rpwd, _ = t.PipelineCache.GetMustString(common.CacheHostRedisPassword)
|
|
var cmd = fmt.Sprintf("%s -h %s -a %s ping", RedisCliFile, localIp, rpwd)
|
|
if pong, _ := runtime.GetRunner().SudoCmd(cmd, false, false); !strings.Contains(pong, "PONG") {
|
|
return fmt.Errorf("failed to connect redis server: %s:6379", localIp)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type EnableRedisService struct {
|
|
common.KubeAction
|
|
}
|
|
|
|
func (t *EnableRedisService) Execute(runtime connector.Runtime) error {
|
|
if _, err := runtime.GetRunner().SudoCmd("sysctl -w vm.overcommit_memory=1 net.core.somaxconn=10240", false, false); err != nil {
|
|
return err
|
|
}
|
|
if _, err := runtime.GetRunner().SudoCmd("systemctl daemon-reload", false, false); err != nil {
|
|
return err
|
|
}
|
|
if _, err := runtime.GetRunner().SudoCmd("systemctl restart redis-server", false, false); err != nil {
|
|
return err
|
|
}
|
|
if _, err := runtime.GetRunner().SudoCmd("systemctl enable redis-server", false, false); err != nil {
|
|
return err
|
|
}
|
|
|
|
var cmd = "( sleep 10 && systemctl --no-pager status redis-server ) || ( systemctl restart redis-server && sleep 3 && systemctl --no-pager status redis-server ) || ( systemctl restart redis-server && sleep 3 && systemctl --no-pager status redis-server )"
|
|
if _, err := runtime.GetRunner().SudoCmd(cmd, false, false); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type GetOrSetRedisConfig struct {
|
|
common.KubeAction
|
|
}
|
|
|
|
func (t *GetOrSetRedisConfig) Execute(runtime connector.Runtime) (err error) {
|
|
var redisPassword string
|
|
redisAddress := runtime.RemoteHost().GetInternalAddress()
|
|
defer func() {
|
|
if err == nil {
|
|
t.PipelineCache.Set(common.CacheHostRedisPassword, redisPassword)
|
|
t.PipelineCache.Set(common.CacheHostRedisAddress, redisAddress)
|
|
}
|
|
}()
|
|
exist, err := runtime.GetRunner().FileExist(RedisConfigFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exist {
|
|
redisPassword, _ = utils.GeneratePassword(16)
|
|
return
|
|
}
|
|
redisPassword, err = getRedisPwdFromConfigFile(runtime)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if redisPassword != "" {
|
|
logger.Debugf("using existing Redis password: %s found in %s", redisPassword, RedisConfigFile)
|
|
return
|
|
}
|
|
logger.Warnf("found Redis config file %s but password is not set, generating a new one", RedisConfigFile)
|
|
redisPassword, _ = utils.GeneratePassword(16)
|
|
return
|
|
}
|
|
|
|
func getRedisPwdFromConfigFile(runtime connector.Runtime) (string, error) {
|
|
var cmd = fmt.Sprintf("cat %s 2>&1 |grep requirepass |cut -d' ' -f2 |tr -d '\n'", RedisConfigFile)
|
|
if res, err := runtime.GetRunner().SudoCmd(cmd, false, false); err != nil {
|
|
return "", errors.Wrap(err, "failed to get redis password")
|
|
} else {
|
|
return res, nil
|
|
}
|
|
}
|
|
|
|
type GenerateRedisService struct {
|
|
common.KubeAction
|
|
}
|
|
|
|
func (t *GenerateRedisService) Execute(runtime connector.Runtime) error {
|
|
redisPassword, ok := t.PipelineCache.GetMustString(common.CacheHostRedisPassword)
|
|
if !ok || redisPassword == "" {
|
|
return errors.New("redis password not set")
|
|
}
|
|
var systemInfo = runtime.GetSystemInfo()
|
|
var localIp = systemInfo.GetLocalIp()
|
|
if !utils.IsExist(RedisRootDir) {
|
|
utils.Mkdir(RedisConfigDir)
|
|
utils.Mkdir(RedisDataDir)
|
|
utils.Mkdir(RedisLogDir)
|
|
utils.Mkdir(RedisRunDir)
|
|
}
|
|
|
|
var data = util.Data{
|
|
"LocalIP": localIp,
|
|
"RootPath": RedisRootDir,
|
|
"Password": redisPassword,
|
|
}
|
|
redisConfStr, err := util.Render(redisTemplates.RedisConf, data)
|
|
if err != nil {
|
|
return errors.Wrap(errors.WithStack(err), "render redis conf template failed")
|
|
}
|
|
if err := util.WriteFile(RedisConfigFile, []byte(redisConfStr), cc.FileMode0640); err != nil {
|
|
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("write redis conf %s failed", RedisConfigFile))
|
|
}
|
|
|
|
data = util.Data{
|
|
"RedisBinPath": RedisServerFile,
|
|
"RootPath": RedisRootDir,
|
|
"RedisConfPath": RedisConfigFile,
|
|
}
|
|
redisServiceStr, err := util.Render(redisTemplates.RedisService, data)
|
|
if err != nil {
|
|
return errors.Wrap(errors.WithStack(err), "render redis service template failed")
|
|
}
|
|
if err := util.WriteFile(RedisServiceFile, []byte(redisServiceStr), cc.FileMode0644); err != nil {
|
|
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("write redis service %s failed", RedisServiceFile))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type CheckRedisExists struct {
|
|
common.KubePrepare
|
|
}
|
|
|
|
func (p *CheckRedisExists) PreCheck(runtime connector.Runtime) (bool, error) {
|
|
if !utils.IsExist(RedisServerInstalledFile) {
|
|
return true, nil
|
|
}
|
|
|
|
if !utils.IsExist(RedisServerFile) {
|
|
return true, nil
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
type InstallRedis struct {
|
|
common.KubeAction
|
|
manifest.ManifestAction
|
|
}
|
|
|
|
func (t *InstallRedis) Execute(runtime connector.Runtime) error {
|
|
compName := "redis"
|
|
version, err := t.getGlibcVersion(runtime)
|
|
if err != nil {
|
|
logger.Warnf("get glibc version error, %v", err)
|
|
} else {
|
|
if version.lessThan("2.32") {
|
|
compName = "redis-231"
|
|
}
|
|
}
|
|
|
|
redis, err := t.Manifest.Get(compName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
path := redis.FilePath(t.BaseDir)
|
|
|
|
if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("rm -rf /tmp/redis-* && cp -f %s /tmp/%s && cd /tmp && tar xf ./%s", path, redis.Filename, redis.Filename), false, false); err != nil {
|
|
return errors.Wrapf(errors.WithStack(err), "untar redis failed")
|
|
}
|
|
|
|
unpackPath := strings.TrimSuffix(redis.Filename, ".tar.gz")
|
|
var cmd = fmt.Sprintf("cd /tmp/%s && cp ./* /usr/local/bin/ && rm -rf ./%s",
|
|
unpackPath, unpackPath)
|
|
if _, err := runtime.GetRunner().SudoCmd(cmd, false, false); err != nil {
|
|
return err
|
|
}
|
|
// if _, err := runtime.GetRunner().SudoCmd("[[ ! -f /usr/local/bin/redis-sentinel ]] && /usr/local/bin/redis-server /usr/local/bin/redis-sentinel || true", false, true); err != nil {
|
|
// return err
|
|
// }
|
|
if exist, err := runtime.GetRunner().FileExist(RedisServerFile); err != nil {
|
|
return err
|
|
} else if !exist {
|
|
if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("ln -s %s %s", RedisServerInstalledFile, RedisServerFile), false, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if exist, err := runtime.GetRunner().FileExist(RedisCliFile); err != nil {
|
|
return err
|
|
} else if !exist {
|
|
if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("ln -s %s %s", RedisCliInstalledFile, RedisCliFile), false, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *InstallRedis) getGlibcVersion(runtime connector.Runtime) (GlibcVersion, error) {
|
|
output, err := runtime.GetRunner().SudoCmd("ldd --version", false, false)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Convert the output to string
|
|
outputStr := string(output)
|
|
|
|
// Find the line containing the glibc version
|
|
lines := strings.Split(outputStr, "\n")
|
|
|
|
for _, line := range lines {
|
|
if strings.Contains(line, "GLIBC") {
|
|
lineToken := strings.Split(strings.TrimSpace(line), " ")
|
|
|
|
version := lineToken[len(lineToken)-1]
|
|
|
|
return GlibcVersion(version), nil
|
|
}
|
|
}
|
|
|
|
return "", errors.New("glibc version not found")
|
|
}
|
|
|
|
type GlibcVersion string
|
|
|
|
func (v GlibcVersion) lessThan(v2 GlibcVersion) bool {
|
|
return compareGLibC(string(v), string(v2)) < 0
|
|
}
|
|
|
|
func compareGLibC(versionA, versionB string) int {
|
|
partsA := strings.Split(versionA, ".")
|
|
partsB := strings.Split(versionB, ".")
|
|
|
|
// Convert major and minor versions to integers
|
|
majorA, _ := strconv.Atoi(partsA[0])
|
|
minorA, _ := strconv.Atoi(partsA[1])
|
|
|
|
majorB, _ := strconv.Atoi(partsB[0])
|
|
minorB, _ := strconv.Atoi(partsB[1])
|
|
|
|
if majorA < majorB {
|
|
return -1
|
|
} else if majorA > majorB {
|
|
return 1
|
|
} else {
|
|
if minorA < minorB {
|
|
return -1
|
|
} else if minorA > minorB {
|
|
return 1
|
|
}
|
|
}
|
|
|
|
return 0 // versions are equal
|
|
}
|
|
|
|
type InstallRedisModule struct {
|
|
common.KubeModule
|
|
manifest.ManifestModule
|
|
Skip bool
|
|
}
|
|
|
|
func (m *InstallRedisModule) IsSkip() bool {
|
|
return m.Skip
|
|
}
|
|
|
|
func (m *InstallRedisModule) Init() {
|
|
m.Name = "InstallRedis"
|
|
|
|
installRedis := &task.RemoteTask{
|
|
Name: "Install",
|
|
Hosts: m.Runtime.GetAllHosts(),
|
|
Prepare: &CheckRedisExists{},
|
|
Action: &InstallRedis{
|
|
ManifestAction: manifest.ManifestAction{
|
|
BaseDir: m.BaseDir,
|
|
Manifest: m.Manifest,
|
|
},
|
|
},
|
|
Parallel: false,
|
|
Retry: 0,
|
|
}
|
|
|
|
getOrSetRedisPassword := &task.LocalTask{
|
|
Name: "GetOrSetRedisConfig",
|
|
Action: new(GetOrSetRedisConfig),
|
|
Retry: 1,
|
|
}
|
|
|
|
configRedis := &task.RemoteTask{
|
|
Name: "Config",
|
|
Hosts: m.Runtime.GetAllHosts(),
|
|
Action: new(GenerateRedisService),
|
|
Parallel: false,
|
|
Retry: 0,
|
|
}
|
|
|
|
enableRedisService := &task.RemoteTask{
|
|
Name: "EnableRedisService",
|
|
Hosts: m.Runtime.GetAllHosts(),
|
|
Action: new(EnableRedisService),
|
|
Parallel: false,
|
|
Retry: 0,
|
|
}
|
|
|
|
checkRedisServiceState := &task.RemoteTask{
|
|
Name: "CheckState",
|
|
Hosts: m.Runtime.GetAllHosts(),
|
|
Action: new(CheckRedisServiceState),
|
|
Parallel: false,
|
|
Retry: 3,
|
|
Delay: 3 * time.Second,
|
|
}
|
|
|
|
m.Tasks = []task.Interface{
|
|
installRedis,
|
|
getOrSetRedisPassword,
|
|
configRedis,
|
|
enableRedisService,
|
|
checkRedisServiceState,
|
|
}
|
|
}
|