mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 07:41:27 +00:00
services.spread distributes replicas across every online, schedulable agent (replica i → agents[i mod n], deterministic). auto placement now orders by real headroom — memory utilisation, cpu usage, then container count — instead of count alone. Lifecycle (start/stop/restart/delete) and GET /runtime are now driven by container_instances inventory, so remote replicas are visible and tear down wherever they run — covers spread, pins, and stale pin leftovers. Spread and explicit pins are mutually exclusive. Generated with [Devin](https://devin.ai) Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
986 lines
26 KiB
Go
986 lines
26 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
|
|
`
|
|
|
|
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,
|
|
)
|
|
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 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,
|
|
)
|
|
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 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,
|
|
)
|
|
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 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,
|
|
); 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 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 listServiceContainers = `-- name: ListServiceContainers :many
|
|
SELECT id, name, node_agent_id, status 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"`
|
|
}
|
|
|
|
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,
|
|
); 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 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 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 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
|
|
`
|
|
|
|
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,
|
|
)
|
|
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 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
|
|
}
|