feat: 优化 PHP 运行环境扩展安装 (#6401)
Some checks failed
sync2gitee / repo-sync (push) Failing after -8m45s

This commit is contained in:
zhengkunwang 2024-09-06 18:37:51 +08:00 committed by GitHub
parent 1af83c1aeb
commit 7aefd0df1a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 106 additions and 46 deletions

View file

@ -12,6 +12,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"regexp" "regexp"
"sort"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -536,10 +537,7 @@ func (r *RuntimeService) OperateRuntime(req request.RuntimeOperate) error {
} }
runtime.Status = constant.RuntimeStopped runtime.Status = constant.RuntimeStopped
case constant.RuntimeRestart: case constant.RuntimeRestart:
if err = runComposeCmdWithLog(constant.RuntimeDown, runtime.GetComposePath(), runtime.GetLogPath()); err != nil { if err = restartRuntime(runtime); err != nil {
return err
}
if err = runComposeCmdWithLog(constant.RuntimeUp, runtime.GetComposePath(), runtime.GetLogPath()); err != nil {
return err return err
} }
if err = SyncRuntimeContainerStatus(runtime); err != nil { if err = SyncRuntimeContainerStatus(runtime); err != nil {
@ -659,27 +657,29 @@ func (r *RuntimeService) GetPHPExtensions(runtimeID uint) (response.PHPExtension
return res, err return res, err
} }
extensions := strings.Split(out, "\n") extensions := strings.Split(out, "\n")
var cleanExtensions []string exitExtensions := make(map[string]string)
for _, ext := range extensions { for _, ext := range extensions {
extStr := strings.TrimSpace(ext) extStr := strings.TrimSpace(ext)
if extStr != "" && extStr != "[Zend Modules]" && extStr != "[PHP Modules]" { if extStr != "" && extStr != "[Zend Modules]" && extStr != "[PHP Modules]" {
cleanExtensions = append(cleanExtensions, extStr) exitExtensions[strings.ToLower(extStr)] = extStr
} }
} }
res.Extensions = cleanExtensions
var phpExtensions []response.SupportExtension var phpExtensions []response.SupportExtension
if err = json.Unmarshal(nginx_conf.PHPExtensionsJson, &phpExtensions); err != nil { if err = json.Unmarshal(nginx_conf.PHPExtensionsJson, &phpExtensions); err != nil {
return res, err return res, err
} }
for _, ext := range phpExtensions { for _, ext := range phpExtensions {
for _, cExt := range cleanExtensions { if _, ok := exitExtensions[strings.ToLower(ext.Check)]; ok {
if ext.Check == cExt { ext.Installed = true
ext.Installed = true
break
}
} }
res.SupportExtensions = append(res.SupportExtensions, ext) res.SupportExtensions = append(res.SupportExtensions, ext)
} }
for _, name := range exitExtensions {
res.Extensions = append(res.Extensions, name)
}
sort.Slice(res.Extensions, func(i, j int) bool {
return strings.ToLower(res.Extensions[i]) < strings.ToLower(res.Extensions[j])
})
return res, nil return res, nil
} }
@ -688,7 +688,7 @@ func (r *RuntimeService) InstallPHPExtension(req request.PHPExtensionInstallReq)
if err != nil { if err != nil {
return err return err
} }
installTask, err := task.NewTaskWithOps(req.Name, task.TaskInstall, task.TaskScopeRuntime, req.TaskID, runtime.ID) installTask, err := task.NewTaskWithOps(req.Name, task.TaskInstall, task.TaskScopeRuntimeExtension, req.TaskID, runtime.ID)
if err != nil { if err != nil {
return err return err
} }
@ -698,6 +698,27 @@ func (r *RuntimeService) InstallPHPExtension(req request.PHPExtensionInstallReq)
if err != nil { if err != nil {
return err return err
} }
client, err := docker.NewClient()
defer client.Close()
if err == nil {
oldImageID, err := client.GetImageIDByName(runtime.Image)
commitCmd := fmt.Sprintf("docker commit %s %s", runtime.ContainerName, runtime.Image)
err = cmd2.ExecWithLogFile(commitCmd, 15*time.Minute, t.Task.LogFile)
if err != nil {
return err
}
newImageID, err := client.GetImageIDByName(runtime.Image)
if err == nil && newImageID != oldImageID {
if err := client.DeleteImage(oldImageID); err != nil {
t.Log(fmt.Sprintf("delete old image error %v", err))
} else {
t.Log("delete old image success")
}
}
}
if err = restartRuntime(runtime); err != nil {
return err
}
return nil return nil
}, nil) }, nil)
go func() { go func() {
@ -745,7 +766,10 @@ func (r *RuntimeService) UnInstallPHPExtension(req request.PHPExtensionInstallRe
if err != nil { if err != nil {
return err return err
} }
if err := unInstallPHPExtension(runtime, []string{req.Name}); err != nil { if err = unInstallPHPExtension(runtime, []string{req.Name}); err != nil {
return err
}
if err = restartRuntime(runtime); err != nil {
return err return err
} }
return runtimeRepo.Save(runtime) return runtimeRepo.Save(runtime)
@ -858,10 +882,7 @@ func (r *RuntimeService) UpdatePHPConfig(req request.PHPConfigUpdate) (err error
return err return err
} }
err = r.OperateRuntime(request.RuntimeOperate{ err = restartRuntime(runtime)
Operate: constant.RuntimeRestart,
ID: req.ID,
})
if err != nil { if err != nil {
_ = fileOp.WriteFile(phpConfigPath, strings.NewReader(string(contentBytes)), 0755) _ = fileOp.WriteFile(phpConfigPath, strings.NewReader(string(contentBytes)), 0755)
return err return err

View file

@ -1,6 +1,7 @@
package service package service
import ( import (
"bufio"
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -135,7 +136,7 @@ func runComposeCmdWithLog(operate string, composePath string, logPath string) er
if operate == "up" { if operate == "up" {
cmd = exec.Command("docker", "compose", "-f", composePath, operate, "-d") cmd = exec.Command("docker", "compose", "-f", composePath, operate, "-d")
} }
logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil { if err != nil {
global.LOG.Errorf("Failed to open log file: %v", err) global.LOG.Errorf("Failed to open log file: %v", err)
return err return err
@ -149,7 +150,7 @@ func runComposeCmdWithLog(operate string, composePath string, logPath string) er
err = cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
return errors.New(buserr.New(constant.ErrRuntimeStart).Error() + ":" + stderrBuf.String()) return errors.New(buserr.New(constant.ErrRuntimeStart).Error() + ":" + err.Error())
} }
return nil return nil
} }
@ -206,21 +207,6 @@ func getRuntimeEnv(envStr, key string) string {
return "" return ""
} }
func getFileEnv(filePath, key string) (string, error) {
envContent, err := files.NewFileOp().GetContent(filePath)
if err != nil {
return "", err
}
env, err := gotenv.Unmarshal(string(envContent))
if err != nil {
return "", err
}
if v, ok := env[key]; ok {
return v, nil
}
return "", nil
}
func buildRuntime(runtime *model.Runtime, oldImageID string, oldEnv string, rebuild bool) { func buildRuntime(runtime *model.Runtime, oldImageID string, oldEnv string, rebuild bool) {
runtimePath := runtime.GetPath() runtimePath := runtime.GetPath()
composePath := runtime.GetComposePath() composePath := runtime.GetComposePath()
@ -249,6 +235,9 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, oldEnv string, rebu
runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + err.Error() runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + err.Error()
} }
} else { } else {
if err = runComposeCmdWithLog(constant.RuntimeDown, runtime.GetComposePath(), runtime.GetLogPath()); err != nil {
return
}
runtime.Message = "" runtime.Message = ""
if oldImageID != "" { if oldImageID != "" {
client, err := docker.NewClient() client, err := docker.NewClient()
@ -500,6 +489,7 @@ func unInstallPHPExtension(runtime *model.Runtime, delExtensions []string) error
delMap[ext.Check] = struct{}{} delMap[ext.Check] = struct{}{}
_ = fileOP.DeleteFile(path.Join(dir, "extensions", ext.File)) _ = fileOP.DeleteFile(path.Join(dir, "extensions", ext.File))
_ = fileOP.DeleteFile(path.Join(dir, "conf", "conf.d", "docker-php-ext-"+ext.Check+".ini")) _ = fileOP.DeleteFile(path.Join(dir, "conf", "conf.d", "docker-php-ext-"+ext.Check+".ini"))
_ = removePHPIniExt(path.Join(dir, "conf", "php.ini"), ext.File)
break break
} }
} }
@ -531,3 +521,48 @@ func unInstallPHPExtension(runtime *model.Runtime, delExtensions []string) error
runtime.Env = envContent runtime.Env = envContent
return nil return nil
} }
func removePHPIniExt(filePath, extensionName string) error {
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
targetLine1 := `extension="` + extensionName + `"`
targetLine2 := `zend_extension="` + extensionName + `"`
tempFile, err := os.CreateTemp(path.Dir(filePath), "temp_*.txt")
if err != nil {
return err
}
defer tempFile.Close()
scanner := bufio.NewScanner(file)
writer := bufio.NewWriter(tempFile)
for scanner.Scan() {
line := scanner.Text()
if !strings.Contains(line, targetLine1) && !strings.Contains(line, targetLine2) {
_, err := writer.WriteString(line + "\n")
if err != nil {
return err
}
}
}
if err := scanner.Err(); err != nil {
return err
}
return os.Rename(tempFile.Name(), filePath)
}
func restartRuntime(runtime *model.Runtime) (err error) {
if err = runComposeCmdWithLog(constant.RuntimeDown, runtime.GetComposePath(), runtime.GetLogPath()); err != nil {
return
}
if err = runComposeCmdWithLog(constant.RuntimeUp, runtime.GetComposePath(), runtime.GetLogPath()); err != nil {
return
}
return
}

View file

@ -54,11 +54,12 @@ const (
) )
const ( const (
TaskScopeWebsite = "Website" TaskScopeWebsite = "Website"
TaskScopeApp = "App" TaskScopeApp = "App"
TaskScopeRuntime = "Runtime" TaskScopeRuntime = "Runtime"
TaskScopeDatabase = "Database" TaskScopeDatabase = "Database"
TaskScopeAppStore = "AppStore" TaskScopeAppStore = "AppStore"
TaskScopeRuntimeExtension = "RuntimeExtension"
) )
const ( const (

View file

@ -8,7 +8,7 @@
}, },
{ {
"name": "opcache", "name": "opcache",
"check": "opcache", "check": "Zend OPcache",
"file": "opcache.so", "file": "opcache.so",
"versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83"], "versions": ["56", "70", "71", "72", "73", "74", "80", "81", "82", "83"],
"installed": false "installed": false

View file

@ -243,4 +243,5 @@ AppStore: "App Store"
TaskSync: "Sync" TaskSync: "Sync"
LocalApp: "Local App" LocalApp: "Local App"
SubTask: "Subtask" SubTask: "Subtask"
RuntimeExtension: "Runtime Extension"

View file

@ -245,4 +245,5 @@ AppStore: "應用商店"
TaskSync: "同步" TaskSync: "同步"
LocalApp: "本地應用" LocalApp: "本地應用"
SubTask: "子任務" SubTask: "子任務"
RuntimeExtension: "運行環境擴展"

View file

@ -246,4 +246,5 @@ EnableSSL: "开启 HTTPS"
AppStore: "应用商店" AppStore: "应用商店"
TaskSync: "同步" TaskSync: "同步"
LocalApp: "本地应用" LocalApp: "本地应用"
SubTask: "子任务" SubTask: "子任务"
RuntimeExtension: "运行环境扩展"

View file

@ -8,7 +8,7 @@
<div class="mt-5"> <div class="mt-5">
<el-text>{{ $t('runtime.popularExtension') }}</el-text> <el-text>{{ $t('runtime.popularExtension') }}</el-text>
</div> </div>
<ComplexTable :data="supportExtensions" @search="search()" :heightDiff="350" :loading="loading"> <ComplexTable :data="supportExtensions" @search="search()" :heightDiff="350" v-loading="loading">
<el-table-column prop="name" :label="$t('commons.table.name')" /> <el-table-column prop="name" :label="$t('commons.table.name')" />
<el-table-column prop="installed" :label="$t('commons.table.status')"> <el-table-column prop="installed" :label="$t('commons.table.status')">
<template #default="{ row }"> <template #default="{ row }">
@ -26,7 +26,7 @@
/> />
</ComplexTable> </ComplexTable>
</DrawerPro> </DrawerPro>
<TaskLog ref="taskLogRef" @clos="search()" /> <TaskLog ref="taskLogRef" @close="search()" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -76,7 +76,7 @@ const installExtension = async (row: Runtime.SupportExtension) => {
}).then(async () => { }).then(async () => {
const req = { const req = {
id: runtime.value.id, id: runtime.value.id,
name: row.check, name: row.name,
taskID: newUUID(), taskID: newUUID(),
}; };
loading.value = true; loading.value = true;
@ -96,7 +96,7 @@ const unInstallPHPExtension = async (row: Runtime.SupportExtension) => {
}).then(async () => { }).then(async () => {
const req = { const req = {
id: runtime.value.id, id: runtime.value.id,
name: row.check, name: row.name,
}; };
loading.value = true; loading.value = true;
try { try {