Files
wehub-resource-sync 75f3dd141c
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CI and Release / lint-frontend (push) Has been cancelled
CI and Release / dockerfile-scan (push) Has been cancelled
CI and Release / test-frontend (push) Has been cancelled
CI and Release / lint-verification-agent (push) Has been cancelled
CI and Release / test-verification-agent (push) Has been cancelled
CI and Release / e2e-verification-agent (push) Has been cancelled
CI and Release / test-backend (push) Has been cancelled
CI and Release / build-dev-image (push) Has been cancelled
CI and Release / push-dev-image (push) Has been cancelled
CI and Release / build-image (push) Has been cancelled
CI and Release / build-verification-image (push) Has been cancelled
CI and Release / determine-version (push) Has been cancelled
CI and Release / push-image (push) Has been cancelled
CI and Release / push-verification-image (12) (push) Has been cancelled
CI and Release / lint-backend (push) Has been cancelled
CI and Release / push-verification-image (17) (push) Has been cancelled
CI and Release / push-verification-image (18) (push) Has been cancelled
CI and Release / release (push) Has been cancelled
CI and Release / publish-helm-chart (push) Has been cancelled
CI and Release / push-verification-image (13) (push) Has been cancelled
CI and Release / push-verification-image (14) (push) Has been cancelled
CI and Release / push-verification-image (15) (push) Has been cancelled
CI and Release / push-verification-image (16) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:11:07 +08:00

109 lines
3.1 KiB
Go

package container
import (
"context"
"fmt"
"io"
"log/slog"
"strconv"
"strings"
"time"
"databasus-verification-agent/internal/features/dbconn"
"databasus-verification-agent/internal/features/restore"
)
const (
containerTerminateTimeout = 30 * time.Second
restoreUser = "postgres"
restoreDB = "postgres"
)
// PostgresContainer exposes two distinct Postgres conns. pg_restore runs
// INSIDE the container and reaches the DB on the container's own loopback
// (GetInContainerConn). The agent-process verifier runs OUTSIDE the container
// and reaches the DB via the random 127.0.0.1 host port the container's 5432
// was published to (GetVerifierConn) — populated by Manager.Spawn after the
// container starts.
type PostgresContainer struct {
engine *dockerEngine
id string
networkID string
hostPort int
password string
log *slog.Logger
}
func (c *PostgresContainer) Exec(
ctx context.Context, cmd []string, stdin io.Reader, env []string,
) (restore.ExecResult, error) {
return c.engine.Exec(ctx, c.id, cmd, stdin, env)
}
func (c *PostgresContainer) GetInContainerConn() dbconn.Conn {
return dbconn.Conn{
Host: "127.0.0.1", Port: pgInternalPort,
User: restoreUser, Password: c.password, Database: restoreDB,
}
}
func (c *PostgresContainer) GetVerifierConn() dbconn.Conn {
return dbconn.Conn{
Host: "127.0.0.1", Port: c.hostPort,
User: restoreUser, Password: c.password, Database: restoreDB,
}
}
// GetDiskUsageBytes sums the byte footprint of the dirs the restore writes
// into: PGDATA (an anonymous volume declared by the postgres image, NOT in
// SizeRw) plus /tmp (where the agent stages the dump). Daemon-reported SizeRw
// alone misses PGDATA entirely; an in-container `du -sb` does not.
func (c *PostgresContainer) GetDiskUsageBytes(ctx context.Context) (int64, error) {
res, err := c.engine.Exec(ctx, c.id,
[]string{"du", "-sb", "/var/lib/postgresql/data", "/tmp"}, nil, nil)
if err != nil {
return 0, fmt.Errorf("exec du: %w", err)
}
if res.ExitCode != 0 {
return 0, fmt.Errorf("du exited %d: %s", res.ExitCode, strings.TrimSpace(res.Stderr))
}
var total int64
for line := range strings.SplitSeq(strings.TrimRight(res.Stdout, "\n"), "\n") {
fields := strings.Fields(line)
if len(fields) == 0 {
continue
}
n, parseErr := strconv.ParseInt(fields[0], 10, 64)
if parseErr != nil {
return 0, fmt.Errorf("parse du line %q: %w", line, parseErr)
}
total += n
}
return total, nil
}
// Terminate removes the container and its network. The caller's context is
// intentionally ignored: teardown runs during job shutdown when that context is
// usually already cancelled, so the removal deadline is rooted in a fresh
// background context. The parameter is kept to satisfy runner.JobContainer.
func (c *PostgresContainer) Terminate(context.Context) error {
ctx, cancel := context.WithTimeout(context.Background(), containerTerminateTimeout)
defer cancel()
rmErr := c.engine.RemoveContainer(ctx, c.id)
if c.networkID != "" {
if netErr := c.engine.RemoveNetwork(ctx, c.networkID); netErr != nil && rmErr == nil {
rmErr = netErr
}
}
return rmErr
}