Files
Tomáš DvořákandDevin 1409186a1f feat(nodes): per-node default domain with auto service hostnames (#65)
node_agents.default_domain gives each agent a base domain. Services
placed on it (explicit pin or spread) with no explicit service_domains
get <name>.<default_domain> — resolved in serviceDomainNames so edge
routing, wake placeholders, and public_url all see the same names.
Spread services union every eligible node's base domain; explicit
domains always win.

Surfaces: PUT /agents/:id {default_domain} (hostname-validated), CLI
nodes update --domain, MCP arg docs, OpenAPI + regenerated types,
node-detail inline editor.

Also marks the already-shipped backup-import roadmap item.

Generated with [Devin](https://devin.ai)

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-10 00:07:30 +02:00

1202 lines
33 KiB
Go

// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: agents.sql
package sqlcdb
import (
"context"
"database/sql"
"encoding/json"
"time"
"github.com/sqlc-dev/pqtype"
)
const clearServiceNodePins = `-- name: ClearServiceNodePins :execrows
UPDATE services SET node_id = NULL, updated_at = NOW() WHERE node_id = $1
`
func (q *Queries) ClearServiceNodePins(ctx context.Context, nodeID sql.NullString) (int64, error) {
result, err := q.db.ExecContext(ctx, clearServiceNodePins, nodeID)
if err != nil {
return 0, err
}
return result.RowsAffected()
}
const completeCommand = `-- name: CompleteCommand :one
UPDATE agent_commands SET
status = $3,
result = $4,
error = $5,
completed_at = NOW(),
updated_at = NOW()
WHERE id = $1 AND node_agent_id = $2
RETURNING id, type, node_agent_id, container_id, payload, status, result, error, created_at, updated_at, completed_at
`
type CompleteCommandParams struct {
ID string `json:"id"`
NodeAgentID string `json:"node_agent_id"`
Status sql.NullString `json:"status"`
Result sql.NullString `json:"result"`
Error sql.NullString `json:"error"`
}
func (q *Queries) CompleteCommand(ctx context.Context, arg CompleteCommandParams) (AgentCommand, error) {
row := q.db.QueryRowContext(ctx, completeCommand,
arg.ID,
arg.NodeAgentID,
arg.Status,
arg.Result,
arg.Error,
)
var i AgentCommand
err := row.Scan(
&i.ID,
&i.Type,
&i.NodeAgentID,
&i.ContainerID,
&i.Payload,
&i.Status,
&i.Result,
&i.Error,
&i.CreatedAt,
&i.UpdatedAt,
&i.CompletedAt,
)
return i, err
}
const createAgent = `-- name: CreateAgent :one
INSERT INTO node_agents (
id, name, hostname, ip_address, port, status, version,
capabilities, resources, last_heartbeat, metadata
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
RETURNING id, name, hostname, ip_address, port, status, version, capabilities, resources, last_heartbeat, created_at, updated_at, metadata, auto_prune, schedulable, tags, default_domain
`
type CreateAgentParams struct {
ID string `json:"id"`
Name string `json:"name"`
Hostname string `json:"hostname"`
IpAddress string `json:"ip_address"`
Port int32 `json:"port"`
Status sql.NullString `json:"status"`
Version sql.NullString `json:"version"`
Capabilities pqtype.NullRawMessage `json:"capabilities"`
Resources pqtype.NullRawMessage `json:"resources"`
LastHeartbeat sql.NullTime `json:"last_heartbeat"`
Metadata pqtype.NullRawMessage `json:"metadata"`
}
func (q *Queries) CreateAgent(ctx context.Context, arg CreateAgentParams) (NodeAgent, error) {
row := q.db.QueryRowContext(ctx, createAgent,
arg.ID,
arg.Name,
arg.Hostname,
arg.IpAddress,
arg.Port,
arg.Status,
arg.Version,
arg.Capabilities,
arg.Resources,
arg.LastHeartbeat,
arg.Metadata,
)
var i NodeAgent
err := row.Scan(
&i.ID,
&i.Name,
&i.Hostname,
&i.IpAddress,
&i.Port,
&i.Status,
&i.Version,
&i.Capabilities,
&i.Resources,
&i.LastHeartbeat,
&i.CreatedAt,
&i.UpdatedAt,
&i.Metadata,
&i.AutoPrune,
&i.Schedulable,
&i.Tags,
&i.DefaultDomain,
)
return i, err
}
const createCommand = `-- name: CreateCommand :one
INSERT INTO agent_commands (
id, type, node_agent_id, container_id, payload, status
) VALUES ($1, $2, $3, $4, $5, 'pending')
RETURNING id, type, node_agent_id, container_id, payload, status, result, error, created_at, updated_at, completed_at
`
type CreateCommandParams struct {
ID string `json:"id"`
Type string `json:"type"`
NodeAgentID string `json:"node_agent_id"`
ContainerID sql.NullString `json:"container_id"`
Payload pqtype.NullRawMessage `json:"payload"`
}
func (q *Queries) CreateCommand(ctx context.Context, arg CreateCommandParams) (AgentCommand, error) {
row := q.db.QueryRowContext(ctx, createCommand,
arg.ID,
arg.Type,
arg.NodeAgentID,
arg.ContainerID,
arg.Payload,
)
var i AgentCommand
err := row.Scan(
&i.ID,
&i.Type,
&i.NodeAgentID,
&i.ContainerID,
&i.Payload,
&i.Status,
&i.Result,
&i.Error,
&i.CreatedAt,
&i.UpdatedAt,
&i.CompletedAt,
)
return i, err
}
const createContainer = `-- name: CreateContainer :one
INSERT INTO container_instances (
id, name, image, project_id, service_id, node_agent_id,
status, resources, ports, environment, volumes, networks,
restart_policy, health_check
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
RETURNING id, name, image, project_id, service_id, node_agent_id, status, resources, ports, environment, volumes, networks, restart_policy, health_check, created_at, started_at, updated_at
`
type CreateContainerParams struct {
ID string `json:"id"`
Name string `json:"name"`
Image string `json:"image"`
ProjectID string `json:"project_id"`
ServiceID string `json:"service_id"`
NodeAgentID string `json:"node_agent_id"`
Status pqtype.NullRawMessage `json:"status"`
Resources pqtype.NullRawMessage `json:"resources"`
Ports pqtype.NullRawMessage `json:"ports"`
Environment pqtype.NullRawMessage `json:"environment"`
Volumes pqtype.NullRawMessage `json:"volumes"`
Networks pqtype.NullRawMessage `json:"networks"`
RestartPolicy pqtype.NullRawMessage `json:"restart_policy"`
HealthCheck pqtype.NullRawMessage `json:"health_check"`
}
func (q *Queries) CreateContainer(ctx context.Context, arg CreateContainerParams) (ContainerInstance, error) {
row := q.db.QueryRowContext(ctx, createContainer,
arg.ID,
arg.Name,
arg.Image,
arg.ProjectID,
arg.ServiceID,
arg.NodeAgentID,
arg.Status,
arg.Resources,
arg.Ports,
arg.Environment,
arg.Volumes,
arg.Networks,
arg.RestartPolicy,
arg.HealthCheck,
)
var i ContainerInstance
err := row.Scan(
&i.ID,
&i.Name,
&i.Image,
&i.ProjectID,
&i.ServiceID,
&i.NodeAgentID,
&i.Status,
&i.Resources,
&i.Ports,
&i.Environment,
&i.Volumes,
&i.Networks,
&i.RestartPolicy,
&i.HealthCheck,
&i.CreatedAt,
&i.StartedAt,
&i.UpdatedAt,
)
return i, err
}
const deleteAgent = `-- name: DeleteAgent :exec
DELETE FROM node_agents WHERE id = $1
`
func (q *Queries) DeleteAgent(ctx context.Context, id string) error {
_, err := q.db.ExecContext(ctx, deleteAgent, id)
return err
}
const deleteServiceContainersOnAgent = `-- name: DeleteServiceContainersOnAgent :exec
DELETE FROM container_instances WHERE node_agent_id = $1 AND service_id = $2
`
type DeleteServiceContainersOnAgentParams struct {
NodeAgentID string `json:"node_agent_id"`
ServiceID string `json:"service_id"`
}
func (q *Queries) DeleteServiceContainersOnAgent(ctx context.Context, arg DeleteServiceContainersOnAgentParams) error {
_, err := q.db.ExecContext(ctx, deleteServiceContainersOnAgent, arg.NodeAgentID, arg.ServiceID)
return err
}
const getAgent = `-- name: GetAgent :one
SELECT id, name, hostname, ip_address, port, status, version, capabilities, resources, last_heartbeat, created_at, updated_at, metadata, auto_prune, schedulable, tags, default_domain FROM node_agents WHERE id = $1
`
func (q *Queries) GetAgent(ctx context.Context, id string) (NodeAgent, error) {
row := q.db.QueryRowContext(ctx, getAgent, id)
var i NodeAgent
err := row.Scan(
&i.ID,
&i.Name,
&i.Hostname,
&i.IpAddress,
&i.Port,
&i.Status,
&i.Version,
&i.Capabilities,
&i.Resources,
&i.LastHeartbeat,
&i.CreatedAt,
&i.UpdatedAt,
&i.Metadata,
&i.AutoPrune,
&i.Schedulable,
&i.Tags,
&i.DefaultDomain,
)
return i, err
}
const getAgentByHostAndIP = `-- name: GetAgentByHostAndIP :one
SELECT id, name, hostname, ip_address, port, status, version, capabilities, resources, last_heartbeat, created_at, updated_at, metadata, auto_prune, schedulable, tags, default_domain FROM node_agents
WHERE hostname = $1 AND ip_address = $2
LIMIT 1
`
type GetAgentByHostAndIPParams struct {
Hostname string `json:"hostname"`
IpAddress string `json:"ip_address"`
}
func (q *Queries) GetAgentByHostAndIP(ctx context.Context, arg GetAgentByHostAndIPParams) (NodeAgent, error) {
row := q.db.QueryRowContext(ctx, getAgentByHostAndIP, arg.Hostname, arg.IpAddress)
var i NodeAgent
err := row.Scan(
&i.ID,
&i.Name,
&i.Hostname,
&i.IpAddress,
&i.Port,
&i.Status,
&i.Version,
&i.Capabilities,
&i.Resources,
&i.LastHeartbeat,
&i.CreatedAt,
&i.UpdatedAt,
&i.Metadata,
&i.AutoPrune,
&i.Schedulable,
&i.Tags,
&i.DefaultDomain,
)
return i, err
}
const getCommandForAgent = `-- name: GetCommandForAgent :one
SELECT id, type, node_agent_id, container_id, payload, status, result, error, created_at, updated_at, completed_at FROM agent_commands
WHERE id = $1 AND node_agent_id = $2
`
type GetCommandForAgentParams struct {
ID string `json:"id"`
NodeAgentID string `json:"node_agent_id"`
}
func (q *Queries) GetCommandForAgent(ctx context.Context, arg GetCommandForAgentParams) (AgentCommand, error) {
row := q.db.QueryRowContext(ctx, getCommandForAgent, arg.ID, arg.NodeAgentID)
var i AgentCommand
err := row.Scan(
&i.ID,
&i.Type,
&i.NodeAgentID,
&i.ContainerID,
&i.Payload,
&i.Status,
&i.Result,
&i.Error,
&i.CreatedAt,
&i.UpdatedAt,
&i.CompletedAt,
)
return i, err
}
const getContainer = `-- name: GetContainer :one
SELECT id, name, image, project_id, service_id, node_agent_id, status, resources, ports, environment, volumes, networks, restart_policy, health_check, created_at, started_at, updated_at FROM container_instances WHERE id = $1
`
func (q *Queries) GetContainer(ctx context.Context, id string) (ContainerInstance, error) {
row := q.db.QueryRowContext(ctx, getContainer, id)
var i ContainerInstance
err := row.Scan(
&i.ID,
&i.Name,
&i.Image,
&i.ProjectID,
&i.ServiceID,
&i.NodeAgentID,
&i.Status,
&i.Resources,
&i.Ports,
&i.Environment,
&i.Volumes,
&i.Networks,
&i.RestartPolicy,
&i.HealthCheck,
&i.CreatedAt,
&i.StartedAt,
&i.UpdatedAt,
)
return i, err
}
const getContainerForAgent = `-- name: GetContainerForAgent :one
SELECT id, name, image, project_id, service_id, node_agent_id, status, resources, ports, environment, volumes, networks, restart_policy, health_check, created_at, started_at, updated_at FROM container_instances
WHERE id = $1 AND node_agent_id = $2
`
type GetContainerForAgentParams struct {
ID string `json:"id"`
NodeAgentID string `json:"node_agent_id"`
}
func (q *Queries) GetContainerForAgent(ctx context.Context, arg GetContainerForAgentParams) (ContainerInstance, error) {
row := q.db.QueryRowContext(ctx, getContainerForAgent, arg.ID, arg.NodeAgentID)
var i ContainerInstance
err := row.Scan(
&i.ID,
&i.Name,
&i.Image,
&i.ProjectID,
&i.ServiceID,
&i.NodeAgentID,
&i.Status,
&i.Resources,
&i.Ports,
&i.Environment,
&i.Volumes,
&i.Networks,
&i.RestartPolicy,
&i.HealthCheck,
&i.CreatedAt,
&i.StartedAt,
&i.UpdatedAt,
)
return i, err
}
const getLastAgentCommandByType = `-- name: GetLastAgentCommandByType :one
SELECT id, type, node_agent_id, container_id, payload, status, result, error, created_at, updated_at, completed_at FROM agent_commands
WHERE node_agent_id = $1 AND type = $2
ORDER BY created_at DESC LIMIT 1
`
type GetLastAgentCommandByTypeParams struct {
NodeAgentID string `json:"node_agent_id"`
Type string `json:"type"`
}
func (q *Queries) GetLastAgentCommandByType(ctx context.Context, arg GetLastAgentCommandByTypeParams) (AgentCommand, error) {
row := q.db.QueryRowContext(ctx, getLastAgentCommandByType, arg.NodeAgentID, arg.Type)
var i AgentCommand
err := row.Scan(
&i.ID,
&i.Type,
&i.NodeAgentID,
&i.ContainerID,
&i.Payload,
&i.Status,
&i.Result,
&i.Error,
&i.CreatedAt,
&i.UpdatedAt,
&i.CompletedAt,
)
return i, err
}
const insertAgentHeartbeat = `-- name: InsertAgentHeartbeat :exec
INSERT INTO agent_heartbeats (
id, node_agent_id, timestamp, status, resources,
container_count, system_load, uptime, version
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
`
type InsertAgentHeartbeatParams struct {
ID string `json:"id"`
NodeAgentID string `json:"node_agent_id"`
Timestamp time.Time `json:"timestamp"`
Status string `json:"status"`
Resources json.RawMessage `json:"resources"`
ContainerCount int32 `json:"container_count"`
SystemLoad json.RawMessage `json:"system_load"`
Uptime int64 `json:"uptime"`
Version string `json:"version"`
}
func (q *Queries) InsertAgentHeartbeat(ctx context.Context, arg InsertAgentHeartbeatParams) error {
_, err := q.db.ExecContext(ctx, insertAgentHeartbeat,
arg.ID,
arg.NodeAgentID,
arg.Timestamp,
arg.Status,
arg.Resources,
arg.ContainerCount,
arg.SystemLoad,
arg.Uptime,
arg.Version,
)
return err
}
const listAgentHeartbeatsSince = `-- name: ListAgentHeartbeatsSince :many
SELECT id, node_agent_id, timestamp, status, resources, container_count, system_load, uptime, version, created_at FROM agent_heartbeats
WHERE node_agent_id = $1 AND timestamp >= $2
ORDER BY timestamp ASC
`
type ListAgentHeartbeatsSinceParams struct {
NodeAgentID string `json:"node_agent_id"`
Timestamp time.Time `json:"timestamp"`
}
func (q *Queries) ListAgentHeartbeatsSince(ctx context.Context, arg ListAgentHeartbeatsSinceParams) ([]AgentHeartbeat, error) {
rows, err := q.db.QueryContext(ctx, listAgentHeartbeatsSince, arg.NodeAgentID, arg.Timestamp)
if err != nil {
return nil, err
}
defer rows.Close()
items := []AgentHeartbeat{}
for rows.Next() {
var i AgentHeartbeat
if err := rows.Scan(
&i.ID,
&i.NodeAgentID,
&i.Timestamp,
&i.Status,
&i.Resources,
&i.ContainerCount,
&i.SystemLoad,
&i.Uptime,
&i.Version,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listAgents = `-- name: ListAgents :many
SELECT id, name, hostname, ip_address, port, status, version, capabilities, resources, last_heartbeat, created_at, updated_at, metadata, auto_prune, schedulable, tags, default_domain FROM node_agents ORDER BY created_at ASC
`
func (q *Queries) ListAgents(ctx context.Context) ([]NodeAgent, error) {
rows, err := q.db.QueryContext(ctx, listAgents)
if err != nil {
return nil, err
}
defer rows.Close()
items := []NodeAgent{}
for rows.Next() {
var i NodeAgent
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Hostname,
&i.IpAddress,
&i.Port,
&i.Status,
&i.Version,
&i.Capabilities,
&i.Resources,
&i.LastHeartbeat,
&i.CreatedAt,
&i.UpdatedAt,
&i.Metadata,
&i.AutoPrune,
&i.Schedulable,
&i.Tags,
&i.DefaultDomain,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listCommandsForAgent = `-- name: ListCommandsForAgent :many
SELECT id, type, node_agent_id, container_id, payload, status, result, error, created_at, updated_at, completed_at FROM agent_commands
WHERE node_agent_id = $1
ORDER BY created_at DESC
`
func (q *Queries) ListCommandsForAgent(ctx context.Context, nodeAgentID string) ([]AgentCommand, error) {
rows, err := q.db.QueryContext(ctx, listCommandsForAgent, nodeAgentID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []AgentCommand{}
for rows.Next() {
var i AgentCommand
if err := rows.Scan(
&i.ID,
&i.Type,
&i.NodeAgentID,
&i.ContainerID,
&i.Payload,
&i.Status,
&i.Result,
&i.Error,
&i.CreatedAt,
&i.UpdatedAt,
&i.CompletedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listContainersForAgent = `-- name: ListContainersForAgent :many
SELECT id, name, image, project_id, service_id, node_agent_id, status, resources, ports, environment, volumes, networks, restart_policy, health_check, created_at, started_at, updated_at FROM container_instances
WHERE node_agent_id = $1
`
func (q *Queries) ListContainersForAgent(ctx context.Context, nodeAgentID string) ([]ContainerInstance, error) {
rows, err := q.db.QueryContext(ctx, listContainersForAgent, nodeAgentID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ContainerInstance{}
for rows.Next() {
var i ContainerInstance
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Image,
&i.ProjectID,
&i.ServiceID,
&i.NodeAgentID,
&i.Status,
&i.Resources,
&i.Ports,
&i.Environment,
&i.Volumes,
&i.Networks,
&i.RestartPolicy,
&i.HealthCheck,
&i.CreatedAt,
&i.StartedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listPendingCommands = `-- name: ListPendingCommands :many
SELECT id, type, node_agent_id, container_id, payload, status, result, error, created_at, updated_at, completed_at FROM agent_commands
WHERE node_agent_id = $1 AND status = 'pending'
ORDER BY created_at ASC
LIMIT 25
`
func (q *Queries) ListPendingCommands(ctx context.Context, nodeAgentID string) ([]AgentCommand, error) {
rows, err := q.db.QueryContext(ctx, listPendingCommands, nodeAgentID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []AgentCommand{}
for rows.Next() {
var i AgentCommand
if err := rows.Scan(
&i.ID,
&i.Type,
&i.NodeAgentID,
&i.ContainerID,
&i.Payload,
&i.Status,
&i.Result,
&i.Error,
&i.CreatedAt,
&i.UpdatedAt,
&i.CompletedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listSchedulableAgentDomains = `-- name: ListSchedulableAgentDomains :many
SELECT DISTINCT default_domain FROM node_agents
WHERE status = 'online' AND schedulable
AND default_domain <> ''
AND tags @> $1::jsonb
ORDER BY default_domain
`
// Base domains of every online, schedulable agent carrying the required
// placement tags — auto-domain candidates for spread services.
func (q *Queries) ListSchedulableAgentDomains(ctx context.Context, dollar_1 json.RawMessage) ([]string, error) {
rows, err := q.db.QueryContext(ctx, listSchedulableAgentDomains, dollar_1)
if err != nil {
return nil, err
}
defer rows.Close()
items := []string{}
for rows.Next() {
var default_domain string
if err := rows.Scan(&default_domain); err != nil {
return nil, err
}
items = append(items, default_domain)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listSchedulableAgents = `-- name: ListSchedulableAgents :many
SELECT id, name FROM node_agents
WHERE status = 'online' AND schedulable
ORDER BY id
`
type ListSchedulableAgentsRow struct {
ID string `json:"id"`
Name string `json:"name"`
}
func (q *Queries) ListSchedulableAgents(ctx context.Context) ([]ListSchedulableAgentsRow, error) {
rows, err := q.db.QueryContext(ctx, listSchedulableAgents)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListSchedulableAgentsRow{}
for rows.Next() {
var i ListSchedulableAgentsRow
if err := rows.Scan(&i.ID, &i.Name); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listSchedulableAgentsMatching = `-- name: ListSchedulableAgentsMatching :many
SELECT id, name FROM node_agents
WHERE status = 'online' AND schedulable
AND tags @> $1::jsonb
ORDER BY id
`
type ListSchedulableAgentsMatchingRow struct {
ID string `json:"id"`
Name string `json:"name"`
}
// Only agents carrying every required placement tag (jsonb array containment).
func (q *Queries) ListSchedulableAgentsMatching(ctx context.Context, dollar_1 json.RawMessage) ([]ListSchedulableAgentsMatchingRow, error) {
rows, err := q.db.QueryContext(ctx, listSchedulableAgentsMatching, dollar_1)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListSchedulableAgentsMatchingRow{}
for rows.Next() {
var i ListSchedulableAgentsMatchingRow
if err := rows.Scan(&i.ID, &i.Name); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listServiceAgents = `-- name: ListServiceAgents :many
SELECT DISTINCT node_agent_id FROM container_instances WHERE service_id = $1
`
// Distinct agents holding inventory rows for a service.
func (q *Queries) ListServiceAgents(ctx context.Context, serviceID string) ([]string, error) {
rows, err := q.db.QueryContext(ctx, listServiceAgents, serviceID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []string{}
for rows.Next() {
var node_agent_id string
if err := rows.Scan(&node_agent_id); err != nil {
return nil, err
}
items = append(items, node_agent_id)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listServiceContainers = `-- name: ListServiceContainers :many
SELECT id, name, node_agent_id, status, ports, resources FROM container_instances
WHERE service_id = $1
`
type ListServiceContainersRow struct {
ID string `json:"id"`
Name string `json:"name"`
NodeAgentID string `json:"node_agent_id"`
Status pqtype.NullRawMessage `json:"status"`
Ports pqtype.NullRawMessage `json:"ports"`
Resources pqtype.NullRawMessage `json:"resources"`
}
func (q *Queries) ListServiceContainers(ctx context.Context, serviceID string) ([]ListServiceContainersRow, error) {
rows, err := q.db.QueryContext(ctx, listServiceContainers, serviceID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListServiceContainersRow{}
for rows.Next() {
var i ListServiceContainersRow
if err := rows.Scan(
&i.ID,
&i.Name,
&i.NodeAgentID,
&i.Status,
&i.Ports,
&i.Resources,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const pickLeastLoadedAgent = `-- name: PickLeastLoadedAgent :one
SELECT a.id FROM node_agents a
LEFT JOIN LATERAL (
SELECT h.container_count FROM agent_heartbeats h
WHERE h.node_agent_id = a.id
ORDER BY h.timestamp DESC LIMIT 1
) h ON true
WHERE a.status = 'online' AND a.schedulable
ORDER BY
COALESCE(
(a.resources->'memory'->>'used')::double precision
/ NULLIF((a.resources->'memory'->>'total')::double precision, 0),
1.0
) ASC,
COALESCE((a.resources->'cpu'->>'usage')::double precision, 100.0) ASC,
COALESCE(h.container_count, 0) ASC,
a.created_at ASC
LIMIT 1
`
// Resource-aware: lowest memory utilisation first, then cpu, then container
// count. Agents without telemetry sort last (NULL → worst score).
func (q *Queries) PickLeastLoadedAgent(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, pickLeastLoadedAgent)
var id string
err := row.Scan(&id)
return id, err
}
const pickLeastLoadedAgentMatching = `-- name: PickLeastLoadedAgentMatching :one
SELECT a.id FROM node_agents a
LEFT JOIN LATERAL (
SELECT h.container_count FROM agent_heartbeats h
WHERE h.node_agent_id = a.id
ORDER BY h.timestamp DESC LIMIT 1
) h ON true
WHERE a.status = 'online' AND a.schedulable
AND a.tags @> $1::jsonb
ORDER BY
COALESCE(
(a.resources->'memory'->>'used')::double precision
/ NULLIF((a.resources->'memory'->>'total')::double precision, 0),
1.0
) ASC,
COALESCE((a.resources->'cpu'->>'usage')::double precision, 100.0) ASC,
COALESCE(h.container_count, 0) ASC,
a.created_at ASC
LIMIT 1
`
// Same resource-aware ordering as PickLeastLoadedAgent, restricted to agents
// carrying every required placement tag.
func (q *Queries) PickLeastLoadedAgentMatching(ctx context.Context, dollar_1 json.RawMessage) (string, error) {
row := q.db.QueryRowContext(ctx, pickLeastLoadedAgentMatching, dollar_1)
var id string
err := row.Scan(&id)
return id, err
}
const scrubCommandPayload = `-- name: ScrubCommandPayload :exec
UPDATE agent_commands SET payload = $2, updated_at = NOW() WHERE id = $1
`
type ScrubCommandPayloadParams struct {
ID string `json:"id"`
Payload pqtype.NullRawMessage `json:"payload"`
}
func (q *Queries) ScrubCommandPayload(ctx context.Context, arg ScrubCommandPayloadParams) error {
_, err := q.db.ExecContext(ctx, scrubCommandPayload, arg.ID, arg.Payload)
return err
}
const setAgentDefaultDomain = `-- name: SetAgentDefaultDomain :exec
UPDATE node_agents SET default_domain = $2, updated_at = NOW() WHERE id = $1
`
type SetAgentDefaultDomainParams struct {
ID string `json:"id"`
DefaultDomain string `json:"default_domain"`
}
func (q *Queries) SetAgentDefaultDomain(ctx context.Context, arg SetAgentDefaultDomainParams) error {
_, err := q.db.ExecContext(ctx, setAgentDefaultDomain, arg.ID, arg.DefaultDomain)
return err
}
const setAgentSchedulable = `-- name: SetAgentSchedulable :exec
UPDATE node_agents SET schedulable = $2, updated_at = NOW() WHERE id = $1
`
type SetAgentSchedulableParams struct {
ID string `json:"id"`
Schedulable bool `json:"schedulable"`
}
func (q *Queries) SetAgentSchedulable(ctx context.Context, arg SetAgentSchedulableParams) error {
_, err := q.db.ExecContext(ctx, setAgentSchedulable, arg.ID, arg.Schedulable)
return err
}
const setAgentTags = `-- name: SetAgentTags :exec
UPDATE node_agents SET tags = $2::jsonb, updated_at = NOW() WHERE id = $1
`
type SetAgentTagsParams struct {
ID string `json:"id"`
Column2 json.RawMessage `json:"column_2"`
}
func (q *Queries) SetAgentTags(ctx context.Context, arg SetAgentTagsParams) error {
_, err := q.db.ExecContext(ctx, setAgentTags, arg.ID, arg.Column2)
return err
}
const updateAgent = `-- name: UpdateAgent :one
UPDATE node_agents SET
name = $2,
hostname = $3,
ip_address = $4,
port = $5,
status = $6,
version = $7,
capabilities = $8,
resources = $9,
last_heartbeat = $10,
metadata = $11,
auto_prune = $12,
schedulable = $13,
updated_at = NOW()
WHERE id = $1
RETURNING id, name, hostname, ip_address, port, status, version, capabilities, resources, last_heartbeat, created_at, updated_at, metadata, auto_prune, schedulable, tags, default_domain
`
type UpdateAgentParams struct {
ID string `json:"id"`
Name string `json:"name"`
Hostname string `json:"hostname"`
IpAddress string `json:"ip_address"`
Port int32 `json:"port"`
Status sql.NullString `json:"status"`
Version sql.NullString `json:"version"`
Capabilities pqtype.NullRawMessage `json:"capabilities"`
Resources pqtype.NullRawMessage `json:"resources"`
LastHeartbeat sql.NullTime `json:"last_heartbeat"`
Metadata pqtype.NullRawMessage `json:"metadata"`
AutoPrune bool `json:"auto_prune"`
Schedulable bool `json:"schedulable"`
}
func (q *Queries) UpdateAgent(ctx context.Context, arg UpdateAgentParams) (NodeAgent, error) {
row := q.db.QueryRowContext(ctx, updateAgent,
arg.ID,
arg.Name,
arg.Hostname,
arg.IpAddress,
arg.Port,
arg.Status,
arg.Version,
arg.Capabilities,
arg.Resources,
arg.LastHeartbeat,
arg.Metadata,
arg.AutoPrune,
arg.Schedulable,
)
var i NodeAgent
err := row.Scan(
&i.ID,
&i.Name,
&i.Hostname,
&i.IpAddress,
&i.Port,
&i.Status,
&i.Version,
&i.Capabilities,
&i.Resources,
&i.LastHeartbeat,
&i.CreatedAt,
&i.UpdatedAt,
&i.Metadata,
&i.AutoPrune,
&i.Schedulable,
&i.Tags,
&i.DefaultDomain,
)
return i, err
}
const updateAgentHeartbeat = `-- name: UpdateAgentHeartbeat :exec
UPDATE node_agents SET
status = $2,
resources = $3,
last_heartbeat = $4,
updated_at = NOW()
WHERE id = $1
`
type UpdateAgentHeartbeatParams struct {
ID string `json:"id"`
Status sql.NullString `json:"status"`
Resources pqtype.NullRawMessage `json:"resources"`
LastHeartbeat sql.NullTime `json:"last_heartbeat"`
}
func (q *Queries) UpdateAgentHeartbeat(ctx context.Context, arg UpdateAgentHeartbeatParams) error {
_, err := q.db.ExecContext(ctx, updateAgentHeartbeat,
arg.ID,
arg.Status,
arg.Resources,
arg.LastHeartbeat,
)
return err
}
const updateContainerPorts = `-- name: UpdateContainerPorts :exec
UPDATE container_instances SET status = $2, ports = $3, updated_at = NOW()
WHERE id = $1
`
type UpdateContainerPortsParams struct {
ID string `json:"id"`
Status pqtype.NullRawMessage `json:"status"`
Ports pqtype.NullRawMessage `json:"ports"`
}
// Agent reports the real host bindings after create — persist them with
// the status flip so ingress can route to the assigned port.
func (q *Queries) UpdateContainerPorts(ctx context.Context, arg UpdateContainerPortsParams) error {
_, err := q.db.ExecContext(ctx, updateContainerPorts, arg.ID, arg.Status, arg.Ports)
return err
}
const updateContainerStateByName = `-- name: UpdateContainerStateByName :exec
UPDATE container_instances SET
status = jsonb_set(status, '{state}', $3::jsonb),
resources = jsonb_set(COALESCE(resources, '{}'::jsonb), '{net_bytes}', $4::jsonb),
updated_at = NOW()
WHERE name = $1 AND node_agent_id = $2
AND status->>'state' IS DISTINCT FROM 'removed'
`
type UpdateContainerStateByNameParams struct {
Name string `json:"name"`
NodeAgentID string `json:"node_agent_id"`
Column3 json.RawMessage `json:"column_3"`
Column4 json.RawMessage `json:"column_4"`
}
// Agent heartbeat reports real docker state + net counters; reconcile
// inventory for rows this node owns. Tombstones win — never resurrect a
// removed replica. $4 is the cumulative rx+tx byte counter.
func (q *Queries) UpdateContainerStateByName(ctx context.Context, arg UpdateContainerStateByNameParams) error {
_, err := q.db.ExecContext(ctx, updateContainerStateByName,
arg.Name,
arg.NodeAgentID,
arg.Column3,
arg.Column4,
)
return err
}
const updateContainerStatus = `-- name: UpdateContainerStatus :exec
UPDATE container_instances SET status = $2, updated_at = NOW()
WHERE id = $1
`
type UpdateContainerStatusParams struct {
ID string `json:"id"`
Status pqtype.NullRawMessage `json:"status"`
}
func (q *Queries) UpdateContainerStatus(ctx context.Context, arg UpdateContainerStatusParams) error {
_, err := q.db.ExecContext(ctx, updateContainerStatus, arg.ID, arg.Status)
return err
}
const upsertServiceContainer = `-- name: UpsertServiceContainer :exec
INSERT INTO container_instances (
id, name, image, project_id, service_id, node_agent_id,
status, resources, ports, environment, volumes, networks,
restart_policy, health_check
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
ON CONFLICT (id) DO UPDATE SET
image = EXCLUDED.image,
status = EXCLUDED.status,
updated_at = NOW()
`
type UpsertServiceContainerParams struct {
ID string `json:"id"`
Name string `json:"name"`
Image string `json:"image"`
ProjectID string `json:"project_id"`
ServiceID string `json:"service_id"`
NodeAgentID string `json:"node_agent_id"`
Status pqtype.NullRawMessage `json:"status"`
Resources pqtype.NullRawMessage `json:"resources"`
Ports pqtype.NullRawMessage `json:"ports"`
Environment pqtype.NullRawMessage `json:"environment"`
Volumes pqtype.NullRawMessage `json:"volumes"`
Networks pqtype.NullRawMessage `json:"networks"`
RestartPolicy pqtype.NullRawMessage `json:"restart_policy"`
HealthCheck pqtype.NullRawMessage `json:"health_check"`
}
func (q *Queries) UpsertServiceContainer(ctx context.Context, arg UpsertServiceContainerParams) error {
_, err := q.db.ExecContext(ctx, upsertServiceContainer,
arg.ID,
arg.Name,
arg.Image,
arg.ProjectID,
arg.ServiceID,
arg.NodeAgentID,
arg.Status,
arg.Resources,
arg.Ports,
arg.Environment,
arg.Volumes,
arg.Networks,
arg.RestartPolicy,
arg.HealthCheck,
)
return err
}