Consolidate backend: drop vendored APwhy, converge on goose + sqlc

Phase 0 cleanup verified end-to-end:

- Delete vendored APwhy runtime (cmd/apwhy, internal/apwhy) and the
  unwired copy (internal/api/server.go + static bundle + cloned
  internal/{storage,analytics,auth,rbac}) — ~7.5k lines of dead code.
- Keep Containr's native internal/gateway; move APwhy management
  routes under /api/v1/gateway/* so they stop colliding with the
  native /services paths. Fix handlers against the real schema:
  UUID ids via RETURNING, integer enabled flags, plan validation,
  service validate endpoint, unique-violation mapping.
- Port agents.go off GORM onto sqlc (sqlc/queries/agents.sql);
  drop gorm dependencies; fix sqlc schema drift (services.environment_id).
- Replace the SELECT 1 goose baseline with the real consolidated
  schema (52 tables) minus APwhy's parallel auth/RBAC objects;
  idempotent via IF NOT EXISTS / OR REPLACE / guarded DO blocks so it
  replays cleanly over legacy installs. Add a drop migration for the
  dead objects and delete the legacy filename-tracked migrations/
  loader, directory, and Dockerfile copy. Verified via cmd/migrate:
  fresh install 53 tables, legacy upgrade clean to 20260917000000.
- Canvas: add invisible React Flow handles to ServiceNode so inferred
  auto-connections stop emitting warning #008.
- Remove reactflow dep (superseded by @xyflow/react), test harness
  cmds, and legacy 0001_init.sql backups.

Authored By: TDvorak <[email protected]>
This commit is contained in:
Tomas Dvorak committed 2026-09-17 09:40:08 +02:00
1 parent 37a56dc6b6
commit 9e3aa3aa17
153 files changed
+6996 -11135

No files matched your search

@@ -11,27 +11,23 @@ import (
)
const (
defaultLegacyMigrationsDir = "migrations"
defaultGooseMigrationsDir = "migrations_goose"
migrationAdvisoryLockKey = int64(637266846588921720)
defaultGooseMigrationsDir = "migrations_goose"
migrationAdvisoryLockKey = int64(637266846588921720)
)
var migrationLockRetryInterval = 250 * time.Millisecond
// MigrateAll runs legacy app migrations first, then goose-managed migrations.
// This allows a safe transition without breaking existing installations.
func (db *DB) MigrateAll(legacyDir, gooseDir string) error {
// MigrateAll runs the goose-managed migrations. goose is the only migration
// path; the legacy filename-tracked system was consolidated into the baseline.
func (db *DB) MigrateAll(gooseDir string) error {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
return db.MigrateAllWithLock(ctx, legacyDir, gooseDir)
return db.MigrateAllWithLock(ctx, gooseDir)
}
// MigrateAllWithLock runs legacy + goose migrations while holding a PostgreSQL
// MigrateAllWithLock runs goose migrations while holding a PostgreSQL
// advisory lock to prevent concurrent migrators from racing.
func (db *DB) MigrateAllWithLock(ctx context.Context, legacyDir, gooseDir string) error {
if legacyDir == "" {
legacyDir = defaultLegacyMigrationsDir
}
func (db *DB) MigrateAllWithLock(ctx context.Context, gooseDir string) error {
if gooseDir == "" {
gooseDir = defaultGooseMigrationsDir
}
@@ -46,15 +42,7 @@ func (db *DB) MigrateAllWithLock(ctx context.Context, legacyDir, gooseDir string
}
}()
if err := db.Migrate(legacyDir); err != nil {
return err
}
if err := db.MigrateGoose(gooseDir); err != nil {
return err
}
return nil
return db.MigrateGoose(gooseDir)
}
func (db *DB) acquireMigrationLock(ctx context.Context) (func() error, error) {
@@ -112,6 +100,10 @@ func (db *DB) MigrateGoose(migrationsDir string) error {
return fmt.Errorf("failed to access goose migrations directory %q: %w", migrationsDir, err)
}
if err := db.ensureRequiredExtensions(); err != nil {
return err
}
if err := goose.SetDialect("postgres"); err != nil {
return fmt.Errorf("failed to set goose dialect: %w", err)
}
-123
View File
@@ -1,73 +1,10 @@
package database
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"sort"
)
var managedMigrationPattern = regexp.MustCompile(`^\d{3}_.+\.sql$`)
// Migrate runs all migration files in the migrations directory
func (db *DB) Migrate(migrationsDir string) error {
if err := db.ensureRequiredExtensions(); err != nil {
return err
}
// Create migrations table if it doesn't exist
if err := db.createMigrationsTable(); err != nil {
return fmt.Errorf("failed to create migrations table: %w", err)
}
migrationFiles, err := listManagedMigrationFiles(migrationsDir)
if err != nil {
return err
}
// Run each migration that hasn't been run yet
for _, fileName := range migrationFiles {
if err := db.runMigration(migrationsDir, fileName); err != nil {
return fmt.Errorf("failed to run migration %s: %w", fileName, err)
}
}
log.Println("All migrations completed successfully")
return nil
}
func listManagedMigrationFiles(migrationsDir string) ([]string, error) {
entries, err := os.ReadDir(migrationsDir)
if err != nil {
return nil, fmt.Errorf("failed to read migrations directory: %w", err)
}
migrationFiles := make([]string, 0, len(entries))
for _, entry := range entries {
if entry.IsDir() {
continue
}
fileName := entry.Name()
if !isManagedMigrationFile(fileName) {
log.Printf("Skipping unmanaged migration file: %s", fileName)
continue
}
migrationFiles = append(migrationFiles, fileName)
}
sort.Strings(migrationFiles)
return migrationFiles, nil
}
func isManagedMigrationFile(fileName string) bool {
return managedMigrationPattern.MatchString(fileName)
}
func (db *DB) ensureRequiredExtensions() error {
if _, err := db.Exec(`CREATE EXTENSION IF NOT EXISTS pgcrypto`); err != nil {
return fmt.Errorf("failed to ensure pgcrypto extension required for UUID defaults: %w", err)
@@ -75,66 +12,6 @@ func (db *DB) ensureRequiredExtensions() error {
return nil
}
func (db *DB) createMigrationsTable() error {
query := `
CREATE TABLE IF NOT EXISTS migrations (
id SERIAL PRIMARY KEY,
filename VARCHAR(255) UNIQUE NOT NULL,
executed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
)
`
_, err := db.Exec(query)
return err
}
func (db *DB) runMigration(migrationsDir, fileName string) error {
// Check if migration has already been run
var count int
err := db.QueryRow("SELECT COUNT(*) FROM migrations WHERE filename = $1", fileName).Scan(&count)
if err != nil {
return fmt.Errorf("failed to check migration status: %w", err)
}
if count > 0 {
log.Printf("Migration %s already executed, skipping", fileName)
return nil
}
// Read migration file
filePath := filepath.Join(migrationsDir, fileName)
content, err := os.ReadFile(filePath)
if err != nil {
return fmt.Errorf("failed to read migration file %s: %w", fileName, err)
}
// Execute migration in a transaction
tx, err := db.BeginTx(context.Background(), nil)
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
// Execute migration SQL
_, err = tx.Exec(string(content))
if err != nil {
return fmt.Errorf("failed to execute migration %s: %w", fileName, err)
}
// Record that migration was executed
_, err = tx.Exec("INSERT INTO migrations (filename) VALUES ($1)", fileName)
if err != nil {
return fmt.Errorf("failed to record migration %s: %w", fileName, err)
}
// Commit transaction
if err = tx.Commit(); err != nil {
return fmt.Errorf("failed to commit migration %s: %w", fileName, err)
}
log.Printf("Successfully executed migration: %s", fileName)
return nil
}
// SeedData inserts initial data for development
func (db *DB) SeedData() error {
// Check if we already have users
@@ -1,63 +0,0 @@
package database
import (
"os"
"path/filepath"
"reflect"
"testing"
)
func TestIsManagedMigrationFile(t *testing.T) {
cases := []struct {
name string
fileName string
want bool
}{
{name: "valid triple digit migration", fileName: "001_initial_schema.sql", want: true},
{name: "supports higher migration number", fileName: "120_add_table.sql", want: true},
{name: "legacy sqlite migration excluded", fileName: "0001_init.sql", want: false},
{name: "backup file excluded", fileName: "001_initial_schema.sql.bak", want: false},
{name: "non migration sql excluded", fileName: "seed.sql", want: false},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := isManagedMigrationFile(tc.fileName)
if got != tc.want {
t.Fatalf("isManagedMigrationFile(%q) = %v, want %v", tc.fileName, got, tc.want)
}
})
}
}
func TestListManagedMigrationFiles(t *testing.T) {
dir := t.TempDir()
files := []string{
"020_add_database_templates.sql",
"001_initial_schema.sql",
"0001_init.sql",
"README.txt",
"021_expand_database_service_types.sql",
}
for _, file := range files {
path := filepath.Join(dir, file)
if err := os.WriteFile(path, []byte("-- test"), 0o644); err != nil {
t.Fatalf("failed to write temp file %s: %v", file, err)
}
}
got, err := listManagedMigrationFiles(dir)
if err != nil {
t.Fatalf("listManagedMigrationFiles returned error: %v", err)
}
want := []string{
"001_initial_schema.sql",
"020_add_database_templates.sql",
"021_expand_database_service_types.sql",
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("listManagedMigrationFiles = %v, want %v", got, want)
}
}
@@ -0,0 +1,733 @@
// 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 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
`
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,
)
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 getAgent = `-- name: GetAgent :one
SELECT id, name, hostname, ip_address, port, status, version, capabilities, resources, last_heartbeat, created_at, updated_at, metadata 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,
)
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 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,
)
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 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 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,
); 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 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,
updated_at = NOW()
WHERE id = $1
RETURNING id, name, hostname, ip_address, port, status, version, capabilities, resources, last_heartbeat, created_at, updated_at, metadata
`
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"`
}
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,
)
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,
)
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
}
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.31.1
// source: databases.sql
package sqlcdb
+1 -1
View File
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.31.1
package sqlcdb
+92 -13
View File
@@ -1,17 +1,65 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.31.1
package sqlcdb
import (
"database/sql"
"encoding/json"
"time"
"github.com/google/uuid"
"github.com/sqlc-dev/pqtype"
)
type AgentCommand 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"`
Status sql.NullString `json:"status"`
Result sql.NullString `json:"result"`
Error sql.NullString `json:"error"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
CompletedAt sql.NullTime `json:"completed_at"`
}
type AgentHeartbeat 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"`
CreatedAt time.Time `json:"created_at"`
}
type ContainerInstance 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"`
CreatedAt sql.NullTime `json:"created_at"`
StartedAt sql.NullTime `json:"started_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
}
type DatabaseBackup struct {
ID string `json:"id"`
DatabaseID string `json:"database_id"`
@@ -62,6 +110,22 @@ type EnvironmentVariable struct {
UpdatedAt sql.NullTime `json:"updated_at"`
}
type NodeAgent 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"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
Metadata pqtype.NullRawMessage `json:"metadata"`
}
type Project struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
@@ -80,18 +144,33 @@ type ProjectMember struct {
}
type Service struct {
ID uuid.UUID `json:"id"`
ProjectID uuid.UUID `json:"project_id"`
Name string `json:"name"`
Type string `json:"type"`
Status string `json:"status"`
Image sql.NullString `json:"image"`
Command sql.NullString `json:"command"`
Environment sql.NullString `json:"environment"`
Cpu sql.NullString `json:"cpu"`
Memory sql.NullString `json:"memory"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Description sql.NullString `json:"description"`
ProjectID uuid.UUID `json:"project_id"`
EnvironmentID uuid.UUID `json:"environment_id"`
ServiceType string `json:"service_type"`
SourceType string `json:"source_type"`
SourceUrl sql.NullString `json:"source_url"`
ImageName sql.NullString `json:"image_name"`
BuildCommand sql.NullString `json:"build_command"`
StartCommand sql.NullString `json:"start_command"`
CpuLimit sql.NullInt32 `json:"cpu_limit"`
MemoryLimit sql.NullInt32 `json:"memory_limit"`
PublicUrl sql.NullString `json:"public_url"`
HealthCheckUrl sql.NullString `json:"health_check_url"`
Status sql.NullString `json:"status"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
Type sql.NullString `json:"type"`
Image sql.NullString `json:"image"`
Command sql.NullString `json:"command"`
Environment sql.NullString `json:"environment"`
GitRepo sql.NullString `json:"git_repo"`
GitBranch sql.NullString `json:"git_branch"`
BuildPath sql.NullString `json:"build_path"`
Cpu sql.NullString `json:"cpu"`
Memory sql.NullString `json:"memory"`
}
type ServiceTemplate struct {
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.31.1
// source: projects.sql
package sqlcdb
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.31.1
package sqlcdb
@@ -11,16 +11,26 @@ import (
)
type Querier interface {
CompleteCommand(ctx context.Context, arg CompleteCommandParams) (AgentCommand, error)
CountDatabaseServicesByUserAndName(ctx context.Context, arg CountDatabaseServicesByUserAndNameParams) (int64, error)
CountProjectsByUser(ctx context.Context, arg CountProjectsByUserParams) (int64, error)
CountServicesByProjectAndName(ctx context.Context, arg CountServicesByProjectAndNameParams) (int64, error)
CreateAgent(ctx context.Context, arg CreateAgentParams) (NodeAgent, error)
CreateCommand(ctx context.Context, arg CreateCommandParams) (AgentCommand, error)
CreateContainer(ctx context.Context, arg CreateContainerParams) (ContainerInstance, error)
CreateDatabaseBackup(ctx context.Context, arg CreateDatabaseBackupParams) error
CreateDatabaseService(ctx context.Context, arg CreateDatabaseServiceParams) error
CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error)
CreateServiceFromTemplate(ctx context.Context, arg CreateServiceFromTemplateParams) error
DatabaseServiceExistsByIDAndUser(ctx context.Context, arg DatabaseServiceExistsByIDAndUserParams) (bool, error)
DeleteAgent(ctx context.Context, id string) error
DeleteDatabaseServiceByIDAndUser(ctx context.Context, arg DeleteDatabaseServiceByIDAndUserParams) error
DeleteProjectByID(ctx context.Context, projectID uuid.UUID) (int64, error)
GetAgent(ctx context.Context, id string) (NodeAgent, error)
GetAgentByHostAndIP(ctx context.Context, arg GetAgentByHostAndIPParams) (NodeAgent, error)
GetCommandForAgent(ctx context.Context, arg GetCommandForAgentParams) (AgentCommand, error)
GetContainer(ctx context.Context, id string) (ContainerInstance, error)
GetContainerForAgent(ctx context.Context, arg GetContainerForAgentParams) (ContainerInstance, error)
GetDatabaseBackupByIDAndDatabaseAndUser(ctx context.Context, arg GetDatabaseBackupByIDAndDatabaseAndUserParams) (DatabaseBackup, error)
GetDatabaseServiceByIDAndUser(ctx context.Context, arg GetDatabaseServiceByIDAndUserParams) (GetDatabaseServiceByIDAndUserRow, error)
GetProjectByIDForUser(ctx context.Context, arg GetProjectByIDForUserParams) (Project, error)
@@ -28,9 +38,15 @@ type Querier interface {
GetProjectOwnerID(ctx context.Context, id uuid.UUID) (uuid.UUID, error)
GetProjectRoleForUser(ctx context.Context, arg GetProjectRoleForUserParams) (string, error)
GetServiceTemplateByID(ctx context.Context, id string) (ServiceTemplate, error)
InsertAgentHeartbeat(ctx context.Context, arg InsertAgentHeartbeatParams) error
InsertProjectEnvironment(ctx context.Context, arg InsertProjectEnvironmentParams) error
ListAgentHeartbeatsSince(ctx context.Context, arg ListAgentHeartbeatsSinceParams) ([]AgentHeartbeat, error)
ListAgents(ctx context.Context) ([]NodeAgent, error)
ListCommandsForAgent(ctx context.Context, nodeAgentID string) ([]AgentCommand, error)
ListContainersForAgent(ctx context.Context, nodeAgentID string) ([]ContainerInstance, error)
ListDatabaseBackupsByDatabaseAndUser(ctx context.Context, arg ListDatabaseBackupsByDatabaseAndUserParams) ([]DatabaseBackup, error)
ListDatabaseServicesByUser(ctx context.Context, userID string) ([]ListDatabaseServicesByUserRow, error)
ListPendingCommands(ctx context.Context, nodeAgentID string) ([]AgentCommand, error)
ListProjectsWithStatsByUser(ctx context.Context, arg ListProjectsWithStatsByUserParams) ([]ListProjectsWithStatsByUserRow, error)
ListServiceTemplates(ctx context.Context) ([]ServiceTemplate, error)
ListServiceTemplatesByCategory(ctx context.Context, category string) ([]ServiceTemplate, error)
@@ -38,6 +54,9 @@ type Querier interface {
SetDatabaseServiceStatusAndConnectionByID(ctx context.Context, arg SetDatabaseServiceStatusAndConnectionByIDParams) error
SetDatabaseServiceStatusByID(ctx context.Context, arg SetDatabaseServiceStatusByIDParams) error
SetDatabaseServiceStatusByIDAndUser(ctx context.Context, arg SetDatabaseServiceStatusByIDAndUserParams) error
UpdateAgent(ctx context.Context, arg UpdateAgentParams) (NodeAgent, error)
UpdateAgentHeartbeat(ctx context.Context, arg UpdateAgentHeartbeatParams) error
UpdateContainerStatus(ctx context.Context, arg UpdateContainerStatusParams) error
UpdateDatabaseServiceNameAndPlanByIDAndUser(ctx context.Context, arg UpdateDatabaseServiceNameAndPlanByIDAndUserParams) error
UpdateDatabaseServiceNameByIDAndUser(ctx context.Context, arg UpdateDatabaseServiceNameByIDAndUserParams) error
UpdateDatabaseServicePlanByIDAndUser(ctx context.Context, arg UpdateDatabaseServicePlanByIDAndUserParams) error
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// sqlc v1.31.1
// source: templates.sql
package sqlcdb
@@ -86,8 +86,8 @@ type CreateServiceFromTemplateParams struct {
ImageName sql.NullString `json:"image_name"`
BuildCommand sql.NullString `json:"build_command"`
StartCommand sql.NullString `json:"start_command"`
Type string `json:"type"`
Status string `json:"status"`
Type sql.NullString `json:"type"`
Status sql.NullString `json:"status"`
Image sql.NullString `json:"image"`
Command sql.NullString `json:"command"`
Environment sql.NullString `json:"environment"`