mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-25 11:39:16 +08:00
366 lines
10 KiB
Go
366 lines
10 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
|
"github.com/1Panel-dev/1Panel/backend/global"
|
|
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
|
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
|
_ "github.com/go-sql-driver/mysql"
|
|
)
|
|
|
|
type RedisService struct{}
|
|
|
|
type IRedisService interface {
|
|
UpdateConf(req dto.RedisConfUpdate) error
|
|
UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error
|
|
ChangePassword(info dto.ChangeDBInfo) error
|
|
|
|
LoadStatus() (*dto.RedisStatus, error)
|
|
LoadConf() (*dto.RedisConf, error)
|
|
LoadPersistenceConf() (*dto.RedisPersistence, error)
|
|
|
|
Backup() error
|
|
SearchBackupListWithPage(req dto.PageInfo) (int64, interface{}, error)
|
|
Recover(req dto.RedisBackupRecover) error
|
|
}
|
|
|
|
func NewIRedisService() IRedisService {
|
|
return &RedisService{}
|
|
}
|
|
|
|
func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
|
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var confs []redisConfig
|
|
confs = append(confs, redisConfig{key: "timeout", value: req.Timeout})
|
|
confs = append(confs, redisConfig{key: "maxclients", value: req.Maxclients})
|
|
confs = append(confs, redisConfig{key: "maxmemory", value: req.Maxmemory})
|
|
if err := confSet(redisInfo.Name, confs); err != nil {
|
|
return err
|
|
}
|
|
if _, err := compose.Restart(fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (u *RedisService) ChangePassword(req dto.ChangeDBInfo) error {
|
|
if err := updateInstallInfoInDB("redis", "", "password", true, req.Value); err != nil {
|
|
return err
|
|
}
|
|
if err := updateInstallInfoInDB("redis-commander", "", "password", true, req.Value); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error {
|
|
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var confs []redisConfig
|
|
if req.Type == "rbd" {
|
|
confs = append(confs, redisConfig{key: "save", value: req.Save})
|
|
} else {
|
|
confs = append(confs, redisConfig{key: "appendonly", value: req.Appendonly})
|
|
confs = append(confs, redisConfig{key: "appendfsync", value: req.Appendfsync})
|
|
}
|
|
if err := confSet(redisInfo.Name, confs); err != nil {
|
|
return err
|
|
}
|
|
if _, err := compose.Restart(fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (u *RedisService) LoadStatus() (*dto.RedisStatus, error) {
|
|
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
commands := append(redisExec(redisInfo.ContainerName, redisInfo.Password), "info")
|
|
cmd := exec.Command("docker", commands...)
|
|
stdout, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return nil, errors.New(string(stdout))
|
|
}
|
|
rows := strings.Split(string(stdout), "\r\n")
|
|
rowMap := make(map[string]string)
|
|
for _, v := range rows {
|
|
itemRow := strings.Split(v, ":")
|
|
if len(itemRow) == 2 {
|
|
rowMap[itemRow[0]] = itemRow[1]
|
|
}
|
|
}
|
|
var info dto.RedisStatus
|
|
arr, err := json.Marshal(rowMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_ = json.Unmarshal(arr, &info)
|
|
return &info, nil
|
|
}
|
|
|
|
func (u *RedisService) LoadConf() (*dto.RedisConf, error) {
|
|
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var item dto.RedisConf
|
|
item.ContainerName = redisInfo.ContainerName
|
|
item.Name = redisInfo.Name
|
|
item.Port = redisInfo.Port
|
|
item.Requirepass = redisInfo.Password
|
|
item.Timeout, _ = configGetStr(redisInfo.ContainerName, redisInfo.Password, "timeout")
|
|
item.Maxclients, _ = configGetStr(redisInfo.ContainerName, redisInfo.Password, "maxclients")
|
|
item.Maxmemory, _ = configGetStr(redisInfo.ContainerName, redisInfo.Password, "maxmemory")
|
|
return &item, nil
|
|
}
|
|
|
|
func (u *RedisService) LoadPersistenceConf() (*dto.RedisPersistence, error) {
|
|
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var item dto.RedisPersistence
|
|
if item.Appendonly, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly"); err != nil {
|
|
return nil, err
|
|
}
|
|
if item.Appendfsync, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendfsync"); err != nil {
|
|
return nil, err
|
|
}
|
|
if item.Save, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "save"); err != nil {
|
|
return nil, err
|
|
}
|
|
return &item, nil
|
|
}
|
|
|
|
func (u *RedisService) Backup() error {
|
|
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
stdout, err := cmd.Execf("docker exec %s redis-cli -a %s --no-auth-warning save", redisInfo.ContainerName, redisInfo.Password)
|
|
if err != nil {
|
|
return errors.New(string(stdout))
|
|
}
|
|
localDir, err := loadLocalDir()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
backupDir := fmt.Sprintf("database/redis/%s/", redisInfo.Name)
|
|
fullDir := fmt.Sprintf("%s/%s", localDir, backupDir)
|
|
if _, err := os.Stat(fullDir); err != nil && os.IsNotExist(err) {
|
|
if err = os.MkdirAll(fullDir, os.ModePerm); err != nil {
|
|
if err != nil {
|
|
return fmt.Errorf("mkdir %s failed, err: %v", fullDir, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
appendonly, err := configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
global.LOG.Infof("appendonly in redis conf is %s", appendonly)
|
|
if appendonly == "yes" {
|
|
redisDataDir := fmt.Sprintf("%s/%s/%s/data", constant.AppInstallDir, "redis", redisInfo.Name)
|
|
name := fmt.Sprintf("%s.tar.gz", time.Now().Format("20060102150405"))
|
|
if err := handleTar(redisDataDir+"/appendonlydir", fullDir, name, ""); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
name := fmt.Sprintf("%s.rdb", time.Now().Format("20060102150405"))
|
|
stdout1, err1 := cmd.Execf("docker cp %s:/data/dump.rdb %s/%s", redisInfo.ContainerName, fullDir, name)
|
|
if err1 != nil {
|
|
return errors.New(string(stdout1))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (u *RedisService) Recover(req dto.RedisBackupRecover) error {
|
|
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
appendonly, err := configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
global.LOG.Infof("appendonly in redis conf is %s", appendonly)
|
|
|
|
composeDir := fmt.Sprintf("%s/redis/%s", constant.AppInstallDir, redisInfo.Name)
|
|
if _, err := compose.Down(composeDir + "/docker-compose.yml"); err != nil {
|
|
return err
|
|
}
|
|
fullName := fmt.Sprintf("%s/%s", req.FileDir, req.FileName)
|
|
if appendonly == "yes" {
|
|
redisDataDir := fmt.Sprintf("%s/%s/%s/data/", constant.AppInstallDir, "redis", redisInfo.Name)
|
|
if err := handleUnTar(fullName, redisDataDir); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
input, err := ioutil.ReadFile(fullName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = ioutil.WriteFile(composeDir+"/data/dump.rdb", input, 0640); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if _, err := compose.Up(composeDir + "/docker-compose.yml"); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (u *RedisService) SearchBackupListWithPage(req dto.PageInfo) (int64, interface{}, error) {
|
|
var (
|
|
list []dto.DatabaseFileRecords
|
|
backDatas []dto.DatabaseFileRecords
|
|
)
|
|
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
localDir, err := loadLocalDir()
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
backupDir := fmt.Sprintf("%s/database/redis/%s", localDir, redisInfo.Name)
|
|
_ = filepath.Walk(backupDir, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if !info.IsDir() {
|
|
list = append(list, dto.DatabaseFileRecords{
|
|
CreatedAt: info.ModTime().Format("2006-01-02 15:04:05"),
|
|
Size: int(info.Size()),
|
|
FileDir: backupDir,
|
|
FileName: info.Name(),
|
|
})
|
|
}
|
|
return nil
|
|
})
|
|
total, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
|
if start > total {
|
|
backDatas = make([]dto.DatabaseFileRecords, 0)
|
|
} else {
|
|
if end >= total {
|
|
end = total
|
|
}
|
|
backDatas = list[start:end]
|
|
}
|
|
return int64(total), backDatas, nil
|
|
}
|
|
|
|
func configGetStr(containerName, password, param string) (string, error) {
|
|
commands := append(redisExec(containerName, password), []string{"config", "get", param}...)
|
|
cmd := exec.Command("docker", commands...)
|
|
stdout, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return "", errors.New(string(stdout))
|
|
}
|
|
rows := strings.Split(string(stdout), "\r\n")
|
|
for _, v := range rows {
|
|
itemRow := strings.Split(v, "\n")
|
|
if len(itemRow) == 3 {
|
|
return itemRow[1], nil
|
|
}
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
type redisConfig struct {
|
|
key string
|
|
value string
|
|
}
|
|
|
|
func confSet(redisName string, changeConf []redisConfig) error {
|
|
path := fmt.Sprintf("%s/redis/%s/conf/redis.conf", constant.AppInstallDir, redisName)
|
|
lineBytes, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
files := strings.Split(string(lineBytes), "\n")
|
|
|
|
isStartRange := false
|
|
isEndRange := false
|
|
var newFiles []string
|
|
for _, line := range files {
|
|
if !isStartRange {
|
|
if line == "# Redis configuration rewrite by 1Panel" {
|
|
isStartRange = true
|
|
}
|
|
newFiles = append(newFiles, line)
|
|
continue
|
|
}
|
|
if !isEndRange {
|
|
isExist := false
|
|
for _, item := range changeConf {
|
|
if strings.HasPrefix(line, item.key) || strings.HasPrefix(line, "# "+item.key) {
|
|
if item.value == "0" || len(item.value) == 0 {
|
|
newFiles = append(newFiles, fmt.Sprintf("# %s %s", item.key, item.value))
|
|
} else {
|
|
newFiles = append(newFiles, fmt.Sprintf("%s %s", item.key, item.value))
|
|
}
|
|
isExist = true
|
|
break
|
|
}
|
|
}
|
|
if isExist {
|
|
continue
|
|
}
|
|
newFiles = append(newFiles, line)
|
|
if line == "# End Redis configuration rewrite by 1Panel" {
|
|
isEndRange = true
|
|
}
|
|
continue
|
|
}
|
|
newFiles = append(newFiles, line)
|
|
}
|
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0666)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
_, err = file.WriteString(strings.Join(newFiles, "\n"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func redisExec(containerName, password string) []string {
|
|
cmds := []string{"exec", containerName, "redis-cli", "-a", password, "--no-auth-warning"}
|
|
if len(password) == 0 {
|
|
cmds = []string{"exec", containerName, "redis-cli"}
|
|
}
|
|
return cmds
|
|
}
|