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
109 lines
3.1 KiB
Go
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
|
|
}
|