mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-12 18:26:36 +08:00
[修复] 添加对Docker挂载源的验证,确保配置文件和目录的有效性
This commit is contained in:
@@ -116,6 +116,9 @@ type DockerExecutor struct {
|
|||||||
|
|
||||||
func (e *DockerExecutor) Test(ctx context.Context) error {
|
func (e *DockerExecutor) Test(ctx context.Context) error {
|
||||||
slog.Debug("running docker openresty test", "container", e.ContainerName, "image", e.Image)
|
slog.Debug("running docker openresty test", "container", e.ContainerName, "image", e.Image)
|
||||||
|
if err := e.validateMountSources(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
output, err := e.runEphemeralRuntimeCommand(ctx, "-t")
|
output, err := e.runEphemeralRuntimeCommand(ctx, "-t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("docker %s -t failed: %w: %s", dockerRuntimeCommand, err, string(output))
|
return fmt.Errorf("docker %s -t failed: %w: %s", dockerRuntimeCommand, err, string(output))
|
||||||
@@ -182,6 +185,9 @@ func (e *DockerExecutor) removeContainer(ctx context.Context) error {
|
|||||||
|
|
||||||
func (e *DockerExecutor) runContainer(ctx context.Context) error {
|
func (e *DockerExecutor) runContainer(ctx context.Context) error {
|
||||||
slog.Info("starting docker openresty container", "container", e.ContainerName, "image", e.Image)
|
slog.Info("starting docker openresty container", "container", e.ContainerName, "image", e.Image)
|
||||||
|
if err := e.validateMountSources(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
runArgs := []string{
|
runArgs := []string{
|
||||||
"run", "-d",
|
"run", "-d",
|
||||||
"--name", e.ContainerName,
|
"--name", e.ContainerName,
|
||||||
@@ -202,6 +208,58 @@ func (e *DockerExecutor) runContainer(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *DockerExecutor) validateMountSources() error {
|
||||||
|
if err := ensureRegularFile(e.MainConfigPath, "openresty main config"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ensureDirectory(e.RouteConfigDir, "openresty route config dir"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ensureDirectory(e.CertDir, "openresty cert dir"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ensureDirectory(e.LuaDir, "openresty lua dir"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureRegularFile(path string, label string) error {
|
||||||
|
cleanPath := strings.TrimSpace(path)
|
||||||
|
if cleanPath == "" {
|
||||||
|
return fmt.Errorf("%s path is empty", label)
|
||||||
|
}
|
||||||
|
info, err := os.Stat(cleanPath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("%s %q does not exist; run a config apply first so Docker does not create a directory mount source", label, cleanPath)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("stat %s %q failed: %w", label, cleanPath, err)
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
return fmt.Errorf("%s %q is a directory; expected a file for Docker bind mount", label, cleanPath)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureDirectory(path string, label string) error {
|
||||||
|
cleanPath := strings.TrimSpace(path)
|
||||||
|
if cleanPath == "" {
|
||||||
|
return fmt.Errorf("%s path is empty", label)
|
||||||
|
}
|
||||||
|
info, err := os.Stat(cleanPath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("%s %q does not exist; expected a directory for Docker bind mount", label, cleanPath)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("stat %s %q failed: %w", label, cleanPath, err)
|
||||||
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
|
return fmt.Errorf("%s %q is not a directory; expected a directory for Docker bind mount", label, cleanPath)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
MainConfigPath string
|
MainConfigPath string
|
||||||
RouteConfigPath string
|
RouteConfigPath string
|
||||||
|
|||||||
@@ -133,7 +133,27 @@ func TestDockerExecutorCheckHealthFailsWhenContainerStopped(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prepareDockerMountSources(t *testing.T) (string, string, string, string) {
|
||||||
|
t.Helper()
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
mainConfigPath := filepath.Join(tempDir, "nginx.conf")
|
||||||
|
routeConfigDir := filepath.Join(tempDir, "conf.d")
|
||||||
|
certDir := filepath.Join(tempDir, "certs")
|
||||||
|
luaDir := filepath.Join(tempDir, "lua")
|
||||||
|
|
||||||
|
if err := os.WriteFile(mainConfigPath, []byte("events {}\nhttp {}\n"), 0o644); err != nil {
|
||||||
|
t.Fatalf("WriteFile failed: %v", err)
|
||||||
|
}
|
||||||
|
for _, dir := range []string{routeConfigDir, certDir, luaDir} {
|
||||||
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||||
|
t.Fatalf("MkdirAll failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mainConfigPath, routeConfigDir, certDir, luaDir
|
||||||
|
}
|
||||||
|
|
||||||
func TestDockerExecutorStartsContainerWhenMissing(t *testing.T) {
|
func TestDockerExecutorStartsContainerWhenMissing(t *testing.T) {
|
||||||
|
mainConfigPath, routeConfigDir, certDir, luaDir := prepareDockerMountSources(t)
|
||||||
runner := &fakeRunner{
|
runner := &fakeRunner{
|
||||||
runFn: func(name string, args ...string) ([]byte, error) {
|
runFn: func(name string, args ...string) ([]byte, error) {
|
||||||
if len(args) >= 1 && args[0] == "inspect" {
|
if len(args) >= 1 && args[0] == "inspect" {
|
||||||
@@ -146,11 +166,11 @@ func TestDockerExecutorStartsContainerWhenMissing(t *testing.T) {
|
|||||||
DockerBinary: "docker",
|
DockerBinary: "docker",
|
||||||
ContainerName: "openflare-openresty",
|
ContainerName: "openflare-openresty",
|
||||||
Image: "openresty/openresty:alpine",
|
Image: "openresty/openresty:alpine",
|
||||||
MainConfigPath: filepath.Clean("/tmp/nginx.conf"),
|
MainConfigPath: mainConfigPath,
|
||||||
RouteConfigDir: filepath.Clean("/tmp/routes"),
|
RouteConfigDir: routeConfigDir,
|
||||||
CertDir: filepath.Clean("/tmp/certs"),
|
CertDir: certDir,
|
||||||
NginxCertDir: "/etc/nginx/openflare-certs",
|
NginxCertDir: "/etc/nginx/openflare-certs",
|
||||||
LuaDir: filepath.Clean("/tmp/lua"),
|
LuaDir: luaDir,
|
||||||
NginxLuaDir: "/etc/nginx/openflare-lua",
|
NginxLuaDir: "/etc/nginx/openflare-lua",
|
||||||
Runner: runner,
|
Runner: runner,
|
||||||
}
|
}
|
||||||
@@ -171,6 +191,7 @@ func TestDockerExecutorStartsContainerWhenMissing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDockerExecutorStartsStoppedContainer(t *testing.T) {
|
func TestDockerExecutorStartsStoppedContainer(t *testing.T) {
|
||||||
|
mainConfigPath, routeConfigDir, certDir, luaDir := prepareDockerMountSources(t)
|
||||||
runner := &fakeRunner{
|
runner := &fakeRunner{
|
||||||
runFn: func(name string, args ...string) ([]byte, error) {
|
runFn: func(name string, args ...string) ([]byte, error) {
|
||||||
if len(args) >= 2 && args[0] == "inspect" {
|
if len(args) >= 2 && args[0] == "inspect" {
|
||||||
@@ -183,11 +204,11 @@ func TestDockerExecutorStartsStoppedContainer(t *testing.T) {
|
|||||||
DockerBinary: "docker",
|
DockerBinary: "docker",
|
||||||
ContainerName: "openflare-openresty",
|
ContainerName: "openflare-openresty",
|
||||||
Image: "openresty/openresty:alpine",
|
Image: "openresty/openresty:alpine",
|
||||||
MainConfigPath: filepath.Clean("/tmp/nginx.conf"),
|
MainConfigPath: mainConfigPath,
|
||||||
RouteConfigDir: filepath.Clean("/tmp/routes"),
|
RouteConfigDir: routeConfigDir,
|
||||||
CertDir: filepath.Clean("/tmp/certs"),
|
CertDir: certDir,
|
||||||
NginxCertDir: "/etc/nginx/openflare-certs",
|
NginxCertDir: "/etc/nginx/openflare-certs",
|
||||||
LuaDir: filepath.Clean("/tmp/lua"),
|
LuaDir: luaDir,
|
||||||
NginxLuaDir: "/etc/nginx/openflare-lua",
|
NginxLuaDir: "/etc/nginx/openflare-lua",
|
||||||
Runner: runner,
|
Runner: runner,
|
||||||
}
|
}
|
||||||
@@ -211,10 +232,7 @@ func TestDockerExecutorStartsStoppedContainer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDockerExecutorRunContainerMountsManagedFiles(t *testing.T) {
|
func TestDockerExecutorRunContainerMountsManagedFiles(t *testing.T) {
|
||||||
mainConfigPath := filepath.Clean("/tmp/managed/nginx.conf")
|
mainConfigPath, routeConfigDir, certDir, luaDir := prepareDockerMountSources(t)
|
||||||
routeConfigDir := filepath.Clean("/tmp/managed/conf.d")
|
|
||||||
certDir := filepath.Clean("/tmp/managed/certs")
|
|
||||||
luaDir := filepath.Clean("/tmp/managed/lua")
|
|
||||||
runner := &fakeRunner{}
|
runner := &fakeRunner{}
|
||||||
executor := &DockerExecutor{
|
executor := &DockerExecutor{
|
||||||
DockerBinary: "docker",
|
DockerBinary: "docker",
|
||||||
@@ -256,6 +274,7 @@ func TestDockerExecutorRunContainerMountsManagedFiles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDockerExecutorRecreatesContainerOnStartup(t *testing.T) {
|
func TestDockerExecutorRecreatesContainerOnStartup(t *testing.T) {
|
||||||
|
mainConfigPath, routeConfigDir, certDir, luaDir := prepareDockerMountSources(t)
|
||||||
runner := &fakeRunner{
|
runner := &fakeRunner{
|
||||||
runFn: func(name string, args ...string) ([]byte, error) {
|
runFn: func(name string, args ...string) ([]byte, error) {
|
||||||
if len(args) >= 1 && args[0] == "inspect" {
|
if len(args) >= 1 && args[0] == "inspect" {
|
||||||
@@ -268,11 +287,11 @@ func TestDockerExecutorRecreatesContainerOnStartup(t *testing.T) {
|
|||||||
DockerBinary: "docker",
|
DockerBinary: "docker",
|
||||||
ContainerName: "openflare-openresty",
|
ContainerName: "openflare-openresty",
|
||||||
Image: "openresty/openresty:alpine",
|
Image: "openresty/openresty:alpine",
|
||||||
MainConfigPath: filepath.Clean("/tmp/nginx.conf"),
|
MainConfigPath: mainConfigPath,
|
||||||
RouteConfigDir: filepath.Clean("/tmp/routes"),
|
RouteConfigDir: routeConfigDir,
|
||||||
CertDir: filepath.Clean("/tmp/certs"),
|
CertDir: certDir,
|
||||||
NginxCertDir: "/etc/nginx/openflare-certs",
|
NginxCertDir: "/etc/nginx/openflare-certs",
|
||||||
LuaDir: filepath.Clean("/tmp/lua"),
|
LuaDir: luaDir,
|
||||||
NginxLuaDir: "/etc/nginx/openflare-lua",
|
NginxLuaDir: "/etc/nginx/openflare-lua",
|
||||||
OpenrestyObservabilityPort: 18081,
|
OpenrestyObservabilityPort: 18081,
|
||||||
Runner: runner,
|
Runner: runner,
|
||||||
@@ -292,6 +311,73 @@ func TestDockerExecutorRecreatesContainerOnStartup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDockerExecutorRunContainerRejectsMissingMainConfigFile(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
routeConfigDir := filepath.Join(tempDir, "conf.d")
|
||||||
|
certDir := filepath.Join(tempDir, "certs")
|
||||||
|
luaDir := filepath.Join(tempDir, "lua")
|
||||||
|
for _, dir := range []string{routeConfigDir, certDir, luaDir} {
|
||||||
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||||
|
t.Fatalf("MkdirAll failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
executor := &DockerExecutor{
|
||||||
|
DockerBinary: "docker",
|
||||||
|
ContainerName: "openflare-openresty",
|
||||||
|
Image: "openresty/openresty:alpine",
|
||||||
|
MainConfigPath: filepath.Join(tempDir, "nginx.conf"),
|
||||||
|
RouteConfigDir: routeConfigDir,
|
||||||
|
CertDir: certDir,
|
||||||
|
NginxCertDir: "/etc/nginx/openflare-certs",
|
||||||
|
LuaDir: luaDir,
|
||||||
|
NginxLuaDir: "/etc/nginx/openflare-lua",
|
||||||
|
Runner: &fakeRunner{},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := executor.runContainer(context.Background())
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected missing main config file to be rejected")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "run a config apply first") {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDockerExecutorRunContainerRejectsMainConfigDirectory(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
mainConfigPath := filepath.Join(tempDir, "nginx.conf")
|
||||||
|
routeConfigDir := filepath.Join(tempDir, "conf.d")
|
||||||
|
certDir := filepath.Join(tempDir, "certs")
|
||||||
|
luaDir := filepath.Join(tempDir, "lua")
|
||||||
|
for _, dir := range []string{mainConfigPath, routeConfigDir, certDir, luaDir} {
|
||||||
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||||
|
t.Fatalf("MkdirAll failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
executor := &DockerExecutor{
|
||||||
|
DockerBinary: "docker",
|
||||||
|
ContainerName: "openflare-openresty",
|
||||||
|
Image: "openresty/openresty:alpine",
|
||||||
|
MainConfigPath: mainConfigPath,
|
||||||
|
RouteConfigDir: routeConfigDir,
|
||||||
|
CertDir: certDir,
|
||||||
|
NginxCertDir: "/etc/nginx/openflare-certs",
|
||||||
|
LuaDir: luaDir,
|
||||||
|
NginxLuaDir: "/etc/nginx/openflare-lua",
|
||||||
|
Runner: &fakeRunner{},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := executor.runContainer(context.Background())
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected main config directory to be rejected")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "expected a file") {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewExecutorUsesAbsoluteDockerMountPath(t *testing.T) {
|
func TestNewExecutorUsesAbsoluteDockerMountPath(t *testing.T) {
|
||||||
executor := NewExecutor(ExecutorOptions{
|
executor := NewExecutor(ExecutorOptions{
|
||||||
DockerBinary: "docker",
|
DockerBinary: "docker",
|
||||||
|
|||||||
Reference in New Issue
Block a user