2022-07-15 04:26:12 +00:00
|
|
|
package nginx
|
|
|
|
|
2022-07-21 08:02:07 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
2023-01-06 04:17:12 +00:00
|
|
|
"os"
|
2022-07-21 08:02:07 +00:00
|
|
|
|
|
|
|
"npm/internal/config"
|
2023-01-04 05:36:56 +00:00
|
|
|
"npm/internal/entity/certificate"
|
2022-07-21 08:02:07 +00:00
|
|
|
"npm/internal/entity/host"
|
2023-01-04 05:36:56 +00:00
|
|
|
"npm/internal/entity/upstream"
|
2022-07-21 08:02:07 +00:00
|
|
|
"npm/internal/logger"
|
2023-01-04 05:36:56 +00:00
|
|
|
"npm/internal/status"
|
2022-07-21 08:02:07 +00:00
|
|
|
)
|
2022-07-15 04:26:12 +00:00
|
|
|
|
2023-01-06 04:17:12 +00:00
|
|
|
const (
|
|
|
|
DeletedSuffix = ".deleted"
|
|
|
|
DisabledSuffix = ".disabled"
|
|
|
|
ErrorSuffix = ".error"
|
|
|
|
)
|
|
|
|
|
2022-07-15 04:26:12 +00:00
|
|
|
// ConfigureHost will attempt to write nginx conf and reload nginx
|
2023-01-06 04:17:12 +00:00
|
|
|
// When a host is disabled or deleted, it will name the file with a suffix
|
|
|
|
// that won't be used by nginx.
|
2022-07-15 04:26:12 +00:00
|
|
|
func ConfigureHost(h host.Model) error {
|
|
|
|
// nolint: errcheck, gosec
|
2023-01-06 01:42:02 +00:00
|
|
|
h.Expand([]string{"certificate", "nginxtemplate", "upstream"})
|
2023-01-04 05:36:56 +00:00
|
|
|
|
|
|
|
var certificateTemplate certificate.Template
|
|
|
|
if h.Certificate != nil {
|
|
|
|
certificateTemplate = h.Certificate.GetTemplate()
|
|
|
|
}
|
2022-07-21 08:02:07 +00:00
|
|
|
|
2023-01-09 03:18:11 +00:00
|
|
|
var ups upstream.Model
|
|
|
|
if h.Upstream != nil {
|
|
|
|
ups = *h.Upstream
|
|
|
|
}
|
|
|
|
|
2022-07-21 08:02:07 +00:00
|
|
|
data := TemplateData{
|
2023-01-04 05:36:56 +00:00
|
|
|
Certificate: certificateTemplate,
|
2023-01-06 01:42:02 +00:00
|
|
|
ConfDir: fmt.Sprintf("%s/nginx/hosts", config.Configuration.DataFolder),
|
|
|
|
Config: Config{ // todo
|
2023-05-11 23:40:45 +00:00
|
|
|
Ipv4: !config.Configuration.DisableIPV4,
|
|
|
|
Ipv6: !config.Configuration.DisableIPV6,
|
2023-01-06 01:42:02 +00:00
|
|
|
},
|
|
|
|
DataDir: config.Configuration.DataFolder,
|
|
|
|
Host: h.GetTemplate(),
|
2023-01-09 03:18:11 +00:00
|
|
|
Upstream: ups,
|
2022-07-21 08:02:07 +00:00
|
|
|
}
|
|
|
|
|
2023-01-06 04:17:12 +00:00
|
|
|
removeHostFiles(h)
|
|
|
|
filename := getHostFilename(h, "")
|
2023-05-26 01:04:43 +00:00
|
|
|
// if h.IsDeleted {
|
|
|
|
// filename = getHostFilename(h, DeletedSuffix)
|
|
|
|
// } else if h.IsDisabled {
|
|
|
|
if h.IsDisabled {
|
2023-01-06 04:17:12 +00:00
|
|
|
filename = getHostFilename(h, DisabledSuffix)
|
|
|
|
}
|
2022-07-21 08:02:07 +00:00
|
|
|
|
|
|
|
// Write the config to disk
|
2023-01-06 04:17:12 +00:00
|
|
|
err := writeTemplate(filename, h.NginxTemplate.Template, data, "")
|
2022-07-21 08:02:07 +00:00
|
|
|
if err != nil {
|
|
|
|
// this configuration failed somehow
|
2023-01-04 05:36:56 +00:00
|
|
|
h.Status = status.StatusError
|
2022-07-21 08:02:07 +00:00
|
|
|
h.ErrorMessage = fmt.Sprintf("Template generation failed: %s", err.Error())
|
|
|
|
logger.Debug(h.ErrorMessage)
|
|
|
|
return h.Save(true)
|
|
|
|
}
|
2022-07-15 04:26:12 +00:00
|
|
|
|
2023-01-06 04:17:12 +00:00
|
|
|
// Reload Nginx and check for errors
|
2023-01-04 05:36:56 +00:00
|
|
|
if output, err := reloadNginx(); err != nil {
|
2022-07-21 08:02:07 +00:00
|
|
|
// reloading nginx failed, likely due to this host having a problem
|
2023-01-04 05:36:56 +00:00
|
|
|
h.Status = status.StatusError
|
|
|
|
h.ErrorMessage = fmt.Sprintf("Nginx configuation error: %s - %s", err.Error(), output)
|
2023-01-06 04:17:12 +00:00
|
|
|
|
|
|
|
// Write the .error file, if this isn't a deleted or disabled host
|
|
|
|
// as the reload will only fail because of this host, if it's enabled
|
2023-05-26 01:04:43 +00:00
|
|
|
if !h.IsDisabled {
|
2023-01-06 04:17:12 +00:00
|
|
|
filename = getHostFilename(h, ErrorSuffix)
|
|
|
|
// Clear existing file(s) again
|
|
|
|
removeHostFiles(h)
|
|
|
|
// Write the template again, but with an error message at the end of the file
|
|
|
|
// nolint: errcheck, gosec
|
|
|
|
writeTemplate(filename, h.NginxTemplate.Template, data, h.ErrorMessage)
|
|
|
|
}
|
|
|
|
|
2022-07-21 08:02:07 +00:00
|
|
|
logger.Debug(h.ErrorMessage)
|
|
|
|
} else {
|
|
|
|
// All good
|
2023-01-04 05:36:56 +00:00
|
|
|
h.Status = status.StatusOK
|
2022-07-21 08:02:07 +00:00
|
|
|
h.ErrorMessage = ""
|
|
|
|
logger.Debug("ConfigureHost OK: %+v", h)
|
|
|
|
}
|
|
|
|
|
|
|
|
return h.Save(true)
|
2022-07-15 04:26:12 +00:00
|
|
|
}
|
2023-01-04 05:36:56 +00:00
|
|
|
|
|
|
|
// ConfigureUpstream will attempt to write nginx conf and reload nginx
|
|
|
|
func ConfigureUpstream(u upstream.Model) error {
|
|
|
|
logger.Debug("ConfigureUpstream: %+v)", u)
|
|
|
|
|
|
|
|
// nolint: errcheck, gosec
|
|
|
|
u.Expand([]string{"nginxtemplate"})
|
|
|
|
|
|
|
|
data := TemplateData{
|
|
|
|
ConfDir: fmt.Sprintf("%s/nginx/upstreams", config.Configuration.DataFolder),
|
|
|
|
DataDir: config.Configuration.DataFolder,
|
|
|
|
Upstream: u,
|
|
|
|
}
|
|
|
|
|
2023-01-06 04:17:12 +00:00
|
|
|
removeUpstreamFiles(u)
|
|
|
|
filename := getUpstreamFilename(u, "")
|
2023-05-26 01:04:43 +00:00
|
|
|
// if u.IsDeleted {
|
|
|
|
// filename = getUpstreamFilename(u, DeletedSuffix)
|
|
|
|
// }
|
2023-01-04 05:36:56 +00:00
|
|
|
|
|
|
|
// Write the config to disk
|
2023-01-06 04:17:12 +00:00
|
|
|
err := writeTemplate(filename, u.NginxTemplate.Template, data, "")
|
2023-01-04 05:36:56 +00:00
|
|
|
if err != nil {
|
|
|
|
// this configuration failed somehow
|
|
|
|
u.Status = status.StatusError
|
|
|
|
u.ErrorMessage = fmt.Sprintf("Template generation failed: %s", err.Error())
|
|
|
|
logger.Debug(u.ErrorMessage)
|
|
|
|
return u.Save(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
// nolint: errcheck, gosec
|
|
|
|
if output, err := reloadNginx(); err != nil {
|
|
|
|
// reloading nginx failed, likely due to this host having a problem
|
|
|
|
u.Status = status.StatusError
|
|
|
|
u.ErrorMessage = fmt.Sprintf("Nginx configuation error: %s - %s", err.Error(), output)
|
2023-01-06 04:17:12 +00:00
|
|
|
|
|
|
|
// Write the .error file, if this isn't a deleted upstream
|
|
|
|
// as the reload will only fail because of this upstream
|
2023-05-26 01:04:43 +00:00
|
|
|
// if !u.IsDeleted {
|
|
|
|
filename = getUpstreamFilename(u, ErrorSuffix)
|
|
|
|
// Clear existing file(s) again
|
|
|
|
removeUpstreamFiles(u)
|
|
|
|
// Write the template again, but with an error message at the end of the file
|
|
|
|
// nolint: errcheck, gosec
|
|
|
|
writeTemplate(filename, u.NginxTemplate.Template, data, u.ErrorMessage)
|
|
|
|
// }
|
2023-01-06 04:17:12 +00:00
|
|
|
|
2023-01-04 05:36:56 +00:00
|
|
|
logger.Debug(u.ErrorMessage)
|
|
|
|
} else {
|
|
|
|
// All good
|
|
|
|
u.Status = status.StatusOK
|
|
|
|
u.ErrorMessage = ""
|
|
|
|
logger.Debug("ConfigureUpstream OK: %+v", u)
|
|
|
|
}
|
|
|
|
|
|
|
|
return u.Save(true)
|
|
|
|
}
|
2023-01-06 04:17:12 +00:00
|
|
|
|
|
|
|
func getHostFilename(h host.Model, append string) string {
|
|
|
|
confDir := fmt.Sprintf("%s/nginx/hosts", config.Configuration.DataFolder)
|
|
|
|
return fmt.Sprintf("%s/host_%d.conf%s", confDir, h.ID, append)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getUpstreamFilename(u upstream.Model, append string) string {
|
|
|
|
confDir := fmt.Sprintf("%s/nginx/upstreams", config.Configuration.DataFolder)
|
|
|
|
return fmt.Sprintf("%s/upstream_%d.conf%s", confDir, u.ID, append)
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeHostFiles(h host.Model) {
|
|
|
|
removeFiles([]string{
|
|
|
|
getHostFilename(h, ""),
|
|
|
|
getHostFilename(h, DeletedSuffix),
|
|
|
|
getHostFilename(h, DisabledSuffix),
|
|
|
|
getHostFilename(h, ErrorSuffix),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeUpstreamFiles(u upstream.Model) {
|
|
|
|
removeFiles([]string{
|
|
|
|
getUpstreamFilename(u, ""),
|
|
|
|
getUpstreamFilename(u, DeletedSuffix),
|
|
|
|
getUpstreamFilename(u, ErrorSuffix),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeFiles(files []string) {
|
|
|
|
for _, file := range files {
|
|
|
|
if _, err := os.Stat(file); err == nil {
|
|
|
|
// nolint: errcheck, gosec
|
|
|
|
os.Remove(file)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-01-09 05:42:56 +00:00
|
|
|
|
|
|
|
// GetHostConfigContent returns nginx config as it exists on disk
|
|
|
|
func GetHostConfigContent(h host.Model) (string, error) {
|
|
|
|
filename := getHostFilename(h, "")
|
|
|
|
if h.ErrorMessage != "" {
|
|
|
|
filename = getHostFilename(h, ErrorSuffix)
|
|
|
|
}
|
|
|
|
if h.IsDisabled {
|
|
|
|
filename = getHostFilename(h, DisabledSuffix)
|
|
|
|
}
|
2023-05-26 01:04:43 +00:00
|
|
|
// if h.IsDeleted {
|
|
|
|
// filename = getHostFilename(h, DeletedSuffix)
|
|
|
|
// }
|
2023-01-09 05:42:56 +00:00
|
|
|
|
|
|
|
// nolint: gosec
|
|
|
|
cnt, err := os.ReadFile(filename)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(cnt), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetUpstreamConfigContent returns nginx config as it exists on disk
|
|
|
|
func GetUpstreamConfigContent(u upstream.Model) (string, error) {
|
|
|
|
filename := getUpstreamFilename(u, "")
|
|
|
|
if u.ErrorMessage != "" {
|
|
|
|
filename = getUpstreamFilename(u, ErrorSuffix)
|
|
|
|
}
|
2023-05-26 01:04:43 +00:00
|
|
|
// if u.IsDeleted {
|
|
|
|
// filename = getUpstreamFilename(u, DeletedSuffix)
|
|
|
|
// }
|
2023-01-09 05:42:56 +00:00
|
|
|
|
|
|
|
// nolint: gosec
|
|
|
|
cnt, err := os.ReadFile(filename)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return string(cnt), nil
|
|
|
|
}
|