mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-06-11 17:10:28 +00:00
143 lines
4.5 KiB
Go
143 lines
4.5 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/mailcow/mailcow-dockerized/agent/internal/commands"
|
|
"github.com/mailcow/mailcow-dockerized/agent/internal/proc"
|
|
)
|
|
|
|
func init() { Register("postfix", buildPostfix) }
|
|
|
|
// notFoundFragments are substrings emitted by postsuper/postqueue when the
|
|
// requested queue id doesn't live on this node. Broadcast handlers map them
|
|
// to commands.ErrNotFound so the backend can count partial success.
|
|
var notFoundFragments = []string{
|
|
"No such file or directory",
|
|
"no such file",
|
|
"unknown",
|
|
}
|
|
|
|
func postfixHealthProbe(ctx context.Context) error {
|
|
if err := probeSMTPGreeting("127.0.0.1:25", 3*time.Second); err != nil {
|
|
return err
|
|
}
|
|
return probeShell(ctx, 5*time.Second, "postfix", "status")
|
|
}
|
|
|
|
func buildPostfix(sup *proc.Supervisor) *commands.Table {
|
|
t := commands.New("postfix")
|
|
t.HealthProbe = postfixHealthProbe
|
|
|
|
// Override generic reload — `postfix reload` is the canonical operation,
|
|
// not SIGHUP-to-supervisord (which would just rotate logs).
|
|
t.Register("reload", func(ctx context.Context, _ map[string]any) (any, error) {
|
|
r, err := commands.Run(ctx, commands.RunOptions{}, "postfix", "reload")
|
|
return nil, asError(r, err)
|
|
})
|
|
// Lifecycle: stop/start/restart still go through the supervisor.
|
|
if sup != nil {
|
|
t.Register("restart", func(ctx context.Context, _ map[string]any) (any, error) {
|
|
return nil, sup.Restart(ctx)
|
|
})
|
|
t.Register("stop", func(ctx context.Context, _ map[string]any) (any, error) {
|
|
return nil, sup.Stop(ctx)
|
|
})
|
|
t.Register("start", func(ctx context.Context, _ map[string]any) (any, error) {
|
|
return nil, sup.Start()
|
|
})
|
|
}
|
|
|
|
t.Register("exec.mailq", func(ctx context.Context, _ map[string]any) (any, error) {
|
|
r, err := commands.Run(ctx, commands.RunOptions{OutputCap: 8 << 20}, "postqueue", "-j")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if r.ExitCode != 0 {
|
|
return nil, &runError{msg: "postqueue failed: " + r.Stderr}
|
|
}
|
|
// postqueue -j prints one JSON object per line.
|
|
entries := make([]map[string]any, 0)
|
|
for _, line := range strings.Split(strings.TrimSpace(r.Stdout), "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if line == "" {
|
|
continue
|
|
}
|
|
var obj map[string]any
|
|
if err := json.Unmarshal([]byte(line), &obj); err == nil {
|
|
entries = append(entries, obj)
|
|
}
|
|
}
|
|
return map[string]any{"queue": entries}, nil
|
|
})
|
|
|
|
t.Register("exec.flush-queue", func(ctx context.Context, _ map[string]any) (any, error) {
|
|
r, err := commands.Run(ctx, commands.RunOptions{}, "postqueue", "-f")
|
|
return nil, asError(r, err)
|
|
})
|
|
|
|
t.Register("exec.delete-from-queue", func(ctx context.Context, args map[string]any) (any, error) {
|
|
qid, err := commands.ArgString(args, "queue_id")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
r, err := commands.Run(ctx, commands.RunOptions{}, "postsuper", "-d", qid)
|
|
return nil, asNotFoundOrError(r, err)
|
|
})
|
|
|
|
t.Register("exec.hold-queue", func(ctx context.Context, args map[string]any) (any, error) {
|
|
qid, err := commands.ArgString(args, "queue_id")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
r, err := commands.Run(ctx, commands.RunOptions{}, "postsuper", "-h", qid)
|
|
return nil, asNotFoundOrError(r, err)
|
|
})
|
|
|
|
t.Register("exec.unhold-queue", func(ctx context.Context, args map[string]any) (any, error) {
|
|
qid, err := commands.ArgString(args, "queue_id")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
r, err := commands.Run(ctx, commands.RunOptions{}, "postsuper", "-H", qid)
|
|
return nil, asNotFoundOrError(r, err)
|
|
})
|
|
|
|
t.Register("exec.deliver-now", func(ctx context.Context, args map[string]any) (any, error) {
|
|
qid, err := commands.ArgString(args, "queue_id")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
r, err := commands.Run(ctx, commands.RunOptions{}, "postqueue", "-i", qid)
|
|
return nil, asNotFoundOrError(r, err)
|
|
})
|
|
|
|
t.Register("exec.cat-queue", func(ctx context.Context, args map[string]any) (any, error) {
|
|
qid, err := commands.ArgString(args, "queue_id")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
r, err := commands.Run(ctx, commands.RunOptions{OutputCap: 2 << 20}, "postcat", "-q", qid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if r.ExitCode != 0 {
|
|
if matchesAny(r.Stderr, notFoundFragments) {
|
|
return nil, commands.ErrNotFound
|
|
}
|
|
return nil, &runError{msg: "postcat failed: " + r.Stderr}
|
|
}
|
|
return map[string]any{"body": r.Stdout}, nil
|
|
})
|
|
|
|
t.Register("exec.super-delete", func(ctx context.Context, _ map[string]any) (any, error) {
|
|
r, err := commands.Run(ctx, commands.RunOptions{}, "postsuper", "-d", "ALL")
|
|
return nil, asError(r, err)
|
|
})
|
|
|
|
return t
|
|
}
|