Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions agent/app/dto/nginx.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package dto

import (
"time"

"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/components"
)
Expand Down Expand Up @@ -87,9 +89,41 @@ var LBAlgorithms = map[string]struct{}{"ip_hash": {}, "least_conn": {}}
var RealIPKeys = map[string]struct{}{"X-Forwarded-For": {}, "X-Real-IP": {}, "CF-Connecting-IP": {}}

type NginxModule struct {
Name string `json:"name"`
Script string `json:"script"`
Packages []string `json:"packages"`
Params string `json:"params"`
Enable bool `json:"enable"`
Name string `json:"name"`
Script string `json:"script"`
Packages []string `json:"packages"`
Params string `json:"params"`
Enable bool `json:"enable"`
Deleted bool `json:"deleted,omitempty"`
BuildMode string `json:"buildMode,omitempty"`
Provider string `json:"provider,omitempty"`
DynamicSupport string `json:"dynamicSupport,omitempty"`
LoadOrder int `json:"loadOrder,omitempty"`
Builds []NginxModuleBuild `json:"builds,omitempty"`
LastError string `json:"lastError,omitempty"`
}

type NginxModuleBuild struct {
Provider string `json:"provider"`
Status string `json:"status"`
Hash string `json:"hash"`
Target NginxModuleTarget `json:"target"`
Artifacts []NginxModuleArtifact `json:"artifacts,omitempty"`
Error string `json:"error,omitempty"`
BuiltAt time.Time `json:"builtAt,omitempty"`
}

type NginxModuleTarget struct {
Key string `json:"key"`
OpenRestyVersion string `json:"openrestyVersion"`
Architecture string `json:"architecture"`
Image string `json:"image,omitempty"`
ImageDigest string `json:"imageDigest,omitempty"`
BuilderDigest string `json:"builderDigest,omitempty"`
}

type NginxModuleArtifact struct {
Name string `json:"name"`
Path string `json:"path"`
Checksum string `json:"checksum"`
}
21 changes: 13 additions & 8 deletions agent/app/dto/request/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,22 @@ type NginxRedirectUpdate struct {
}

type NginxBuildReq struct {
TaskID string `json:"taskID" validate:"required"`
Mirror string `json:"mirror" validate:"required"`
TaskID string `json:"taskID" validate:"required"`
Mirror string `json:"mirror" validate:"required"`
Modules []string `json:"modules"`
Force bool `json:"force"`
}

type NginxModuleUpdate struct {
Operate string `json:"operate" validate:"required,oneof=create delete update"`
Name string `json:"name" validate:"required"`
Script string `json:"script"`
Packages string `json:"packages"`
Enable bool `json:"enable"`
Params string `json:"params"`
Operate string `json:"operate" validate:"required,oneof=create delete update"`
Name string `json:"name" validate:"required"`
Script string `json:"script"`
Packages string `json:"packages"`
Enable bool `json:"enable"`
Params string `json:"params"`
BuildMode string `json:"buildMode" validate:"omitempty,oneof=auto dynamic static"`
Provider string `json:"provider" validate:"omitempty,oneof=local prebuilt"`
LoadOrder int `json:"loadOrder" validate:"omitempty,min=0,max=9999"`
}

type NginxOperateReq struct {
Expand Down
24 changes: 17 additions & 7 deletions agent/app/dto/response/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,26 @@ type NginxProxyCache struct {
}

type NginxModule struct {
Name string `json:"name"`
Script string `json:"script"`
Packages string `json:"packages"`
Params string `json:"params"`
Enable bool `json:"enable"`
Name string `json:"name"`
Script string `json:"script"`
Packages string `json:"packages"`
Params string `json:"params"`
Enable bool `json:"enable"`
BuildMode string `json:"buildMode"`
Provider string `json:"provider"`
DynamicSupport string `json:"dynamicSupport"`
LoadOrder int `json:"loadOrder"`
BuildStatus string `json:"buildStatus"`
LoadStatus string `json:"loadStatus"`
Compatibility string `json:"compatibility"`
Artifacts []dto.NginxModuleArtifact `json:"artifacts"`
LastError string `json:"lastError"`
}

type NginxBuildConfig struct {
Mirror string `json:"mirror"`
Modules []NginxModule `json:"modules"`
Mirror string `json:"mirror"`
DynamicSupported bool `json:"dynamicSupported"`
Modules []NginxModule `json:"modules"`
}

type NginxConfigRes struct {
Expand Down
165 changes: 101 additions & 64 deletions agent/app/service/app_utils.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package service

import (
"bufio"
"context"
"encoding/base64"
"encoding/json"
Expand Down Expand Up @@ -653,11 +652,58 @@ func handleUpgradeCompose(install model.AppInstall, detail model.AppDetail) (map
if oldServiceValue["restart"] != nil {
serviceValue["restart"] = oldServiceValue["restart"]
}
if install.App.Key == constant.AppOpenresty {
mergeOpenrestyModuleVolumes(serviceValue, oldServiceValue)
}
servicesMap[install.ServiceName] = serviceValue
composeMap["services"] = servicesMap
return composeMap, nil
}

// mergeOpenrestyModuleVolumes carries the dynamic module mounts of the old
// compose over to the upgraded one when it does not declare them, so built
// module artifacts and their load configuration stay mounted across upgrades.
func mergeOpenrestyModuleVolumes(serviceValue, oldServiceValue map[string]interface{}) {
oldVolumes, ok := oldServiceValue["volumes"].([]interface{})
if !ok {
return
}
newVolumes, _ := serviceValue["volumes"].([]interface{})
existing := make(map[string]struct{}, len(newVolumes))
for _, volume := range newVolumes {
if containerPath, ok := composeVolumeContainerPath(volume); ok {
existing[containerPath] = struct{}{}
}
}
for _, volume := range oldVolumes {
containerPath, ok := composeVolumeContainerPath(volume)
if !ok {
continue
}
if !strings.Contains(containerPath, nginxModuleEnabledConfDir) && !strings.Contains(containerPath, "nginx/modules/1panel") {
continue
}
if _, ok = existing[containerPath]; ok {
continue
}
newVolumes = append(newVolumes, volume)
existing[containerPath] = struct{}{}
}
serviceValue["volumes"] = newVolumes
}

func composeVolumeContainerPath(volume interface{}) (string, bool) {
volumeStr, ok := volume.(string)
if !ok {
return "", false
}
parts := strings.Split(volumeStr, ":")
if len(parts) < 2 {
return "", false
}
return parts[1], true
}

func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) {
if detail.DockerCompose == "" {
return "", nil
Expand Down Expand Up @@ -691,74 +737,35 @@ func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string
return string(composeByte), nil
}

func buildNginx(parentTask *task.Task) error {
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
func buildNginx(parentTask *task.Task, nginxInstall model.AppInstall) error {
fileOp := files.NewFileOp()
buildPath := path.Join(nginxInstall.GetPath(), "build")
buildPath := path.Join(nginxInstall.GetPath(), nginxModuleBuildDir)
if !fileOp.Stat(buildPath) {
return buserr.New("ErrBuildDirNotFound")
}
moduleConfigPath := path.Join(buildPath, "module.json")
moduleContent, err := fileOp.GetContent(moduleConfigPath)
modules, err := loadNginxModules(nginxInstall)
if err != nil {
return err
}
var (
modules []dto.NginxModule
addModuleParams []string
addPackages []string
)
if len(moduleContent) > 0 {
_ = json.Unmarshal(moduleContent, &modules)
bashFile, err := os.OpenFile(path.Join(buildPath, "tmp", "pre.sh"), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.DirPerm)
if err != nil {
return err
}
defer bashFile.Close()
bashFileWriter := bufio.NewWriter(bashFile)
for _, module := range modules {
if !module.Enable {
continue
}
_, err = bashFileWriter.WriteString(module.Script + "\n")
if err != nil {
return err
}
addModuleParams = append(addModuleParams, module.Params)
addPackages = append(addPackages, module.Packages...)
}
err = bashFileWriter.Flush()
if err != nil {
previousModules := cloneNginxModules(modules)
staticBuild := hasEnabledStaticNginxModules(modules)
if err = configureStaticNginxModules(nginxInstall, modules, ""); err != nil {
return err
}
if staticBuild {
logStr := fmt.Sprintf("%s %s", i18n.GetMsgByKey("TaskBuild"), i18n.GetMsgByKey("Image"))
parentTask.LogStart(logStr)
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*parentTask), cmd.WithTimeout(120*time.Minute))
if err = cmdMgr.Run("docker", "compose", "-f", nginxInstall.GetComposePath(), "build"); err != nil {
return err
}
parentTask.LogSuccess(logStr)
}
envs, err := gotenv.Read(nginxInstall.GetEnvPath())
modules, err = buildDynamicNginxModules(nginxInstall, modules, nil, false, parentTask)
if err != nil {
return err
}
envs["RESTY_CONFIG_OPTIONS_MORE"] = ""
envs["RESTY_ADD_PACKAGE_BUILDDEPS"] = ""
if len(addModuleParams) > 0 {
envs["RESTY_CONFIG_OPTIONS_MORE"] = strings.Join(addModuleParams, " ")
}
if len(addPackages) > 0 {
envs["RESTY_ADD_PACKAGE_BUILDDEPS"] = strings.Join(addPackages, " ")
}
_ = gotenv.Write(envs, nginxInstall.GetEnvPath())
if len(addModuleParams) == 0 && len(addPackages) == 0 {
return nil
}
logStr := fmt.Sprintf("%s %s", i18n.GetMsgByKey("TaskBuild"), i18n.GetMsgByKey("Image"))
parentTask.LogStart(logStr)
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*parentTask), cmd.WithTimeout(60*time.Minute))
if err = cmdMgr.Run("docker", "compose", "-f", nginxInstall.GetComposePath(), "build"); err != nil {
return err
}
parentTask.LogSuccess(logStr)
return nil
return commitNginxModuleBuilds(nginxInstall, previousModules, modules, false)
}

func upgradeInstall(req request.AppInstallUpgrade) error {
Expand Down Expand Up @@ -858,22 +865,32 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
}
_ = copyAppDetailMissing(fileOp, detailDir, install.GetPath())
if install.App.Key == constant.AppOpenresty {
installBuildDir := path.Join(install.GetPath(), "build")
detailBuildDir := path.Join(detailDir, "build")
installBuildDir := path.Join(install.GetPath(), nginxModuleBuildDir)
detailBuildDir := path.Join(detailDir, nginxModuleBuildDir)
if !fileOp.Stat(installBuildDir) {
if err := fileOp.CreateDir(installBuildDir, constant.DirPerm); err != nil {
return err
}
}
if err := fileOp.DeleteDir(path.Join(installBuildDir, "tmp")); err != nil {
if err := fileOp.DeleteDir(path.Join(installBuildDir, nginxModuleTmpDir)); err != nil {
return err
}
if err := fileOp.CopyDir(path.Join(detailBuildDir, "tmp"), installBuildDir); err != nil {
if err := fileOp.CopyDir(path.Join(detailBuildDir, nginxModuleTmpDir), installBuildDir); err != nil {
return err
}
if err := fileOp.CopyFile(path.Join(detailBuildDir, "Dockerfile"), installBuildDir); err != nil {
return err
}
if fileOp.Stat(path.Join(detailBuildDir, nginxModuleBuilderFile)) {
if err := fileOp.CopyFile(path.Join(detailBuildDir, nginxModuleBuilderFile), installBuildDir); err != nil {
return err
}
}
if fileOp.Stat(path.Join(detailBuildDir, nginxModuleCatalogFile)) {
if err := fileOp.CopyFile(path.Join(detailBuildDir, nginxModuleCatalogFile), installBuildDir); err != nil {
return err
}
}
if err := fileOp.CopyFile(path.Join(detailBuildDir, "nginx.conf"), installBuildDir); err != nil {
return err
}
Expand Down Expand Up @@ -951,6 +968,26 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
}
}

if install.App.Key == constant.AppOpenresty {
modules, moduleErr := loadNginxModules(install)
if moduleErr != nil {
return moduleErr
}
// Build dynamic modules for the target version before stopping the
// current container. Static modules retain the full rebuild path.
if !hasEnabledStaticNginxModules(modules) {
previousModules := cloneNginxModules(modules)
modules, moduleErr = buildDynamicNginxModules(install, modules, nil, false, t)
if moduleErr != nil {
return moduleErr
}
if moduleErr = saveNginxModules(install, modules); moduleErr != nil {
removeNginxModuleOutputsNotReferenced(install, modules, previousModules)
return moduleErr
}
}
}

if out, err := compose.Down(install.GetComposePath()); err != nil {
if out != "" {
upErr = errors.New(out)
Expand Down Expand Up @@ -985,7 +1022,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
}

if install.App.Key == constant.AppOpenresty {
if err = buildNginx(t); err != nil {
if err = buildNginx(t, install); err != nil {
t.Log(err.Error())
return err
}
Expand Down Expand Up @@ -2285,7 +2322,7 @@ func handleOpenrestyFile(appInstall *model.AppInstall) error {

func handleDefaultServer(appInstall *model.AppInstall) error {
installDir := appInstall.GetPath()
defaultConfigPath := path.Join(installDir, "conf", "default", "00.default.conf")
defaultConfigPath := path.Join(installDir, nginxModuleConfDir, "default", "00.default.conf")
fileOp := files.NewFileOp()
content, err := fileOp.GetContent(defaultConfigPath)
if err != nil {
Expand All @@ -2299,7 +2336,7 @@ func handleDefaultServer(appInstall *model.AppInstall) error {
}

func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool, sslRejectHandshake bool) error {
sslDir := path.Join(appInstall.GetPath(), "conf", "ssl")
sslDir := path.Join(appInstall.GetPath(), nginxModuleConfDir, "ssl")
fileOp := files.NewFileOp()
if !fileOp.Stat(sslDir) {
return errors.New("ssl dir not found")
Expand Down Expand Up @@ -2329,7 +2366,7 @@ func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool, sslRe
_ = NewIWebsiteSSLService().Delete([]uint{websiteSSL.ID})
}()
}
defaultConfigPath := path.Join(appInstall.GetPath(), "conf", "default", "00.default.conf")
defaultConfigPath := path.Join(appInstall.GetPath(), nginxModuleConfDir, "default", "00.default.conf")
content, err := os.ReadFile(defaultConfigPath)
if err != nil {
return err
Expand Down
Loading