142 lines
4.4 KiB
Go
142 lines
4.4 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.pcux.cn/xuans/Scert/backend/internal/nginxactivation"
|
|
)
|
|
|
|
func startApplicationNginx(t *testing.T, f *applicationTLSFixture, binary string) nginxactivation.Rotation {
|
|
t.Helper()
|
|
prefix := filepath.Join(f.root, "nginx")
|
|
directory := filepath.Join(prefix, "activation")
|
|
version := filepath.Join(directory, "00000000000000000000000000000001")
|
|
if err := os.MkdirAll(version, 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for destination, source := range map[string]string{"fullchain.pem": f.config.Server.TLSCert, "key.pem": f.config.Server.TLSKey} {
|
|
content, err := os.ReadFile(source)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
writeApplicationTLSFile(t, filepath.Join(version, destination), content)
|
|
}
|
|
include := fmt.Sprintf("ssl_certificate %s/fullchain.pem;\nssl_certificate_key %s/key.pem;\n", version, version)
|
|
writeApplicationTLSFile(t, filepath.Join(directory, "active.conf"), []byte(include))
|
|
configuration := fmt.Sprintf(`worker_processes 1;
|
|
pid %s/nginx.pid;
|
|
error_log stderr warn;
|
|
events { worker_connections 64; }
|
|
http {
|
|
access_log off;
|
|
client_body_temp_path %s/client;
|
|
proxy_temp_path %s/proxy;
|
|
fastcgi_temp_path %s/fastcgi;
|
|
uwsgi_temp_path %s/uwsgi;
|
|
scgi_temp_path %s/scgi;
|
|
server {
|
|
listen %s ssl;
|
|
server_name %s;
|
|
include %s/active.conf;
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
location / {
|
|
proxy_pass https://%s;
|
|
proxy_set_header Host %s;
|
|
proxy_ssl_server_name on;
|
|
proxy_ssl_name %s;
|
|
proxy_ssl_verify on;
|
|
proxy_ssl_trusted_certificate %s;
|
|
}
|
|
}
|
|
}
|
|
`, prefix, prefix, prefix, prefix, prefix, prefix, f.proxy.Address, f.proxy.Hostname,
|
|
directory, f.upstream.Address, f.config.Web.Hostname, f.config.Web.Hostname, f.caPath)
|
|
configPath := filepath.Join(prefix, "nginx.conf")
|
|
writeApplicationTLSFile(t, configPath, []byte(configuration))
|
|
reloader := nginxactivation.CommandReloader{Binary: binary, Config: configPath, Prefix: prefix + "/"}
|
|
ctx, cancel := context.WithTimeout(t.Context(), 15*time.Second)
|
|
defer cancel()
|
|
if err := reloader.Check(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
logPath := filepath.Join(prefix, "process.log")
|
|
log, err := os.OpenFile(logPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o600)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
command := exec.Command(binary, "-p", prefix+"/", "-c", configPath, "-g", "daemon off;")
|
|
command.Stdout, command.Stderr = log, log
|
|
command.Env = []string{"PATH=/usr/sbin:/usr/bin:/sbin:/bin", "LANG=C"}
|
|
command.SysProcAttr = &syscall.SysProcAttr{Pdeathsig: syscall.SIGTERM}
|
|
if err := command.Start(); err != nil {
|
|
_ = log.Close()
|
|
t.Fatal(err)
|
|
}
|
|
done := make(chan error, 1)
|
|
go func() { done <- command.Wait() }()
|
|
t.Cleanup(func() {
|
|
_ = command.Process.Signal(syscall.SIGQUIT)
|
|
select {
|
|
case err := <-done:
|
|
if err != nil {
|
|
t.Errorf("隔离 Nginx 退出失败:%v", err)
|
|
}
|
|
case <-time.After(5 * time.Second):
|
|
_ = command.Process.Kill()
|
|
<-done
|
|
t.Error("隔离 Nginx 未按时退出")
|
|
}
|
|
_ = log.Close()
|
|
if t.Failed() {
|
|
content, _ := os.ReadFile(logPath)
|
|
t.Logf("隔离 Nginx 日志:\n%s", content)
|
|
}
|
|
})
|
|
if err := f.proxy.Wait(ctx, f.oldLeaf); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return nginxactivation.Rotation{Directory: directory, Proxy: f.proxy, Upstream: f.upstream, Reloader: reloader}
|
|
}
|
|
|
|
func startApplicationActivationIPC(t *testing.T, rotation nginxactivation.Rotation) nginxactivation.Client {
|
|
t.Helper()
|
|
directory := t.TempDir()
|
|
if err := os.Chmod(directory, 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path := filepath.Join(directory, "activation.sock")
|
|
listener, err := net.ListenUnix("unix", &net.UnixAddr{Name: path, Net: "unix"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
listener.SetUnlinkOnClose(false)
|
|
if err := os.Chmod(path, 0o600); err != nil {
|
|
_ = listener.Close()
|
|
t.Fatal(err)
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
done := make(chan error, 1)
|
|
service := nginxactivation.LocalService{Rotation: rotation, AllowedUID: uint32(os.Geteuid())}
|
|
go func() { done <- service.Serve(ctx, listener) }()
|
|
t.Cleanup(func() {
|
|
cancel()
|
|
select {
|
|
case err := <-done:
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
case <-time.After(5 * time.Second):
|
|
t.Error("隔离激活服务未按时退出")
|
|
}
|
|
})
|
|
return nginxactivation.Client{SocketPath: path, ServiceUID: uint32(os.Geteuid())}
|
|
}
|