postgre-actor: convert to gatekeeper protocol

This commit is contained in:
Emery Hemingway 2024-06-04 12:24:20 +03:00
parent a40cdbce2c
commit 0b4aa89311
5 changed files with 106 additions and 101 deletions

View File

@ -374,34 +374,37 @@ When called as `assert` (by a symlink or a rename) it will make assertions inste
## PostgreSQL ## PostgreSQL
Readonly access to PostgreSQL databases. Asserts rows as records in response to SQL query assertions. Dynamic updates are not implemented. Readonly access to PostgreSQL databases.
Asserts rows as records in response to SQL query assertions.
Can be disabled by passing `--define:withPostgre=no` to the Nim compiler. Dynamic updates are not implemented.
``` ```
# Configuration example let ?postgreStep = <postgre {connection: [["host" "db.example.com"] ["dbname" "example"] ["user" "hackme"]]}>
<require-service <daemon postgre_actor>>
let ?sqlspace = dataspace
? <service-object <daemon postgre_actor> ?cap> [
$cap <postgre {
dataspace: $sqlspace
connection: [
["host" "example.com"]
["dbname" "foobar"]
["user" "hackme"]
]
}>
]
let ?tuplespace = dataspace let ?tuplespace = dataspace
$tuplespace ? ?row [
$sqlspace <query "SELECT id, name FROM stuff" $tuplespace> $log ! <log "-" { line: $row }>
$tuplespace ? [?id ?name] [
$log ! <log "-" { row: <example-row $id $name> }>
] ]
let ?resolver = dataspace
$resolver ? <accepted ?sqlspace> [
$sqlspace ? <sql-error ?msg ?context> [
$log ! <log "-" { line: $msg context: $context }>
]
$sqlspace <query [SELECT firstname FROM users] $tuplespace>
]
<require-service <daemon postgre-actor>>
$config ? <service-object <daemon postgre-actor> ?cap> [
$cap <resolve $postgreStep $resolver>
]
<daemon postgre-actor {
argv: [ "/bin/postgre-actor" ]
clearEnv: #t
protocol: application/syndicate
}>
``` ```
## preserve_process_environment ## preserve_process_environment

View File

@ -36,9 +36,8 @@ JsonSocketTranslatorStep = <json-socket-translator {
socket: SocketAddress socket: SocketAddress
}>. }>.
PostgreArguments = <postgre { PostgreStep = <postgre {
connection: [PostgreConnectionParameter ...] connection: [PostgreConnectionParameter ...]
dataspace: #:any
}>. }>.
PostgreConnectionParameter = [@key string @val string]. PostgreConnectionParameter = [@key string @val string].
@ -48,9 +47,8 @@ PulseArguments = <pulse {
dataspace: #:any dataspace: #:any
}>. }>.
SqliteArguments = <sqlite { SqliteStep = <sqlite {
database: string database: string
dataspace: #:any
}>. }>.
WebhooksArguments = <webhooks { WebhooksArguments = <webhooks {

View File

@ -1,8 +1,10 @@
# SPDX-FileCopyrightText: ☭ Emery Hemingway # SPDX-FileCopyrightText: ☭ Emery Hemingway
# SPDX-License-Identifier: Unlicense # SPDX-License-Identifier: Unlicense
import preserves, syndicate import
import ./schema/[config, sql] pkg/preserves,
pkg/syndicate, pkg/syndicate/protocols/[gatekeeper, sturdy],
./schema/[config, sql]
{.passL: "-lpq".} {.passL: "-lpq".}
@ -105,54 +107,57 @@ proc renderSql(tokens: openarray[Value]): string =
else: else:
return "" return ""
proc spawnPostgreActor*(turn: Turn; root: Cap): Actor {.discardable.} = proc spawnPostgreActor*(turn: Turn; relay: Cap): Actor {.discardable.} =
spawn("postgre", turn) do (turn: Turn): result = spawnActor(turn, "postgre") do (turn: Turn):
during(turn, root, ?:PostgreArguments) do (params: StringPairs, ds: Cap): let pat = Resolve?:{ 0: PostgreStep.grabTypeFlat, 1: grab() }
var during(turn, relay, pat) do (params: StringPairs, observer: Cap):
conn: PGconn linkActor(turn, path) do (turn: Turn):
statusHandle: Handle var
(keys, vals) = splitParams(params) (keys, vals) = splitParams(params)
conn = PQconnectdbParams(keys, vals, 0) conn = PQconnectdbParams(keys, vals, 0)
checkPointer(conn) checkPointer(conn)
let let
status = PQstatus(conn) status = PQstatus(conn)
msg = $PQerrorMessage(conn) msg = $PQerrorMessage(conn)
statusHandle = publish(turn, ds, deallocCStringArray(keys)
initRecord("status", toSymbol($status), msg.toPreserves)) deallocCStringArray(vals)
if status == CONNECTION_OK: onStop(turn) do (turn: Turn):
during(turn, ds, ?:Query) do (statement: seq[Value], target: Cap): PQfinish(conn)
var text = renderSql statement if status == CONNECTION_OK:
if text == "": let ds = turn.newDataspace()
discard publish(turn, ds, SqlError(msg: "invalid statement", context: $statement)) discard publish(turn, ds, initRecord("status", toSymbol($status), msg.toPreserves))
else: during(turn, ds, ?:Query) do (statement: seq[Value], target: Cap):
var var text = renderSql statement
res = PQexec(conn, text) if text == "":
st = PQresultStatus(res) discard publish(turn, ds, SqlError(msg: "invalid statement", context: $statement))
if st == PGRES_TUPLES_OK or st == PGRES_SINGLE_TUPLE:
let tuples = PQntuples(res)
let fields = PQnfields(res)
if tuples > 0 and fields > 0:
for r in 0..<tuples:
var tupl = initSequence(fields)
for f in 0..<fields:
tupl[f] = toPreserves($PQgetvalue(res, r, f))
discard publish(turn, target, tupl)
else: else:
discard publish(turn, ds, SqlError( var
msg: $PQresStatus(st), res = PQexec(conn, text)
context: $PQresultErrorMessage(res), st = PQresultStatus(res)
)) if st == PGRES_TUPLES_OK or st == PGRES_SINGLE_TUPLE:
PQclear(res) let tuples = PQntuples(res)
else: let fields = PQnfields(res)
stderr.writeLine "refusing to do anything when status is ", status if tuples > 0 and fields > 0:
do: for r in 0..<tuples:
deallocCStringArray(keys) var tupl = initSequence(fields)
deallocCStringArray(vals) for f in 0..<fields:
PQfinish(conn) tupl[f] = toPreserves($PQgetvalue(res, r, f))
discard publish(turn, target, tupl)
else:
discard publish(turn, ds, SqlError(
msg: $PQresStatus(st),
context: $PQresultErrorMessage(res),
))
PQclear(res)
discard publish(turn, observer,
ResolvedAccepted(responderSession: ds))
else:
discard publish(turn, observer,
Rejected(detail: msg.toPreserves))
when isMainModule: when isMainModule:
import syndicate/relays import syndicate/relays
runActor("main") do (turn: Turn): runActor("main") do (turn: Turn):
resolveEnvironment(turn) do (turn: Turn; ds: Cap): resolveEnvironment(turn) do (turn: Turn; relay: Cap):
spawnPostgreActor(turn, ds) spawnPostgreActor(turn, relay)

View File

@ -78,6 +78,12 @@ type
SqliteArguments* {.preservesRecord: "sqlite".} = object SqliteArguments* {.preservesRecord: "sqlite".} = object
`field0`*: SqliteArgumentsField0 `field0`*: SqliteArgumentsField0
PostgreStepField0* {.preservesDictionary.} = object
`connection`*: seq[PostgreConnectionParameter]
PostgreStep* {.preservesRecord: "postgre".} = object
`field0`*: PostgreStepField0
TcpAddress* {.preservesRecord: "tcp".} = object TcpAddress* {.preservesRecord: "tcp".} = object
`host`*: string `host`*: string
`port`*: BiggestInt `port`*: BiggestInt
@ -99,19 +105,16 @@ type
`key`*: string `key`*: string
`val`*: string `val`*: string
PostgreArgumentsField0* {.preservesDictionary.} = object
`connection`*: seq[PostgreConnectionParameter]
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
PostgreArguments* {.preservesRecord: "postgre".} = object
`field0`*: PostgreArgumentsField0
PulseArgumentsField0* {.preservesDictionary.} = object PulseArgumentsField0* {.preservesDictionary.} = object
`dataspace`* {.preservesEmbedded.}: EmbeddedRef `dataspace`* {.preservesEmbedded.}: EmbeddedRef
PulseArguments* {.preservesRecord: "pulse".} = object PulseArguments* {.preservesRecord: "pulse".} = object
`field0`*: PulseArgumentsField0 `field0`*: PulseArgumentsField0
Tcp* {.preservesRecord: "tcp".} = object
`host`*: string
`port`*: BiggestInt
UnixAddress* {.preservesRecord: "unix".} = object UnixAddress* {.preservesRecord: "unix".} = object
`path`*: string `path`*: string
@ -120,10 +123,6 @@ type
PrinterStep* {.preservesRecord: "printer".} = object PrinterStep* {.preservesRecord: "printer".} = object
`field0`*: PrinterStepField0 `field0`*: PrinterStepField0
Tcp* {.preservesRecord: "tcp".} = object
`host`*: string
`port`*: BiggestInt
proc `$`*(x: WebsocketArguments | HttpClientArguments | JsonTranslatorArguments | proc `$`*(x: WebsocketArguments | HttpClientArguments | JsonTranslatorArguments |
SocketAddress | SocketAddress |
Base64DecoderArguments | Base64DecoderArguments |
@ -133,15 +132,15 @@ proc `$`*(x: WebsocketArguments | HttpClientArguments | JsonTranslatorArguments
WebhooksArguments | WebhooksArguments |
FileSystemUsageArguments | FileSystemUsageArguments |
SqliteArguments | SqliteArguments |
PostgreStep |
TcpAddress | TcpAddress |
CacheArguments | CacheArguments |
XmlTranslatorArguments | XmlTranslatorArguments |
PostgreConnectionParameter | PostgreConnectionParameter |
PostgreArguments |
PulseArguments | PulseArguments |
Tcp |
UnixAddress | UnixAddress |
PrinterStep | PrinterStep): string =
Tcp): string =
`$`(toPreserves(x)) `$`(toPreserves(x))
proc encode*(x: WebsocketArguments | HttpClientArguments | proc encode*(x: WebsocketArguments | HttpClientArguments |
@ -154,13 +153,13 @@ proc encode*(x: WebsocketArguments | HttpClientArguments |
WebhooksArguments | WebhooksArguments |
FileSystemUsageArguments | FileSystemUsageArguments |
SqliteArguments | SqliteArguments |
PostgreStep |
TcpAddress | TcpAddress |
CacheArguments | CacheArguments |
XmlTranslatorArguments | XmlTranslatorArguments |
PostgreConnectionParameter | PostgreConnectionParameter |
PostgreArguments |
PulseArguments | PulseArguments |
Tcp |
UnixAddress | UnixAddress |
PrinterStep | PrinterStep): seq[byte] =
Tcp): seq[byte] =
encode(toPreserves(x)) encode(toPreserves(x))

View File

@ -16,13 +16,13 @@ import ./syndesizer/[
xml_translator] xml_translator]
runActor("syndesizer") do (turn: Turn): runActor("syndesizer") do (turn: Turn):
resolveEnvironment(turn) do (turn: Turn; ds: Cap): resolveEnvironment(turn) do (turn: Turn; relay: Cap):
discard spawnTimerDriver(turn, ds) discard spawnTimerDriver(turn, relay)
discard spawnBase64Decoder(turn, ds) discard spawnBase64Decoder(turn, relay)
discard spawnCacheActor(turn, ds) discard spawnCacheActor(turn, relay)
discard spawnFileSystemUsageActor(turn, ds) discard spawnFileSystemUsageActor(turn, relay)
discard spawnHttpDriver(turn, ds) discard spawnHttpDriver(turn, relay)
discard spawnJsonSocketTranslator(turn, ds) discard spawnJsonSocketTranslator(turn, relay)
discard spawnJsonStdioTranslator(turn, ds) discard spawnJsonStdioTranslator(turn, relay)
discard spawnPulseActor(turn, ds) discard spawnPulseActor(turn, relay)
discard spawnXmlTranslator(turn, ds) discard spawnXmlTranslator(turn, relay)