implement basic project support
This commit is contained in:
parent
d00abe5db9
commit
8c66fe69de
2
.envrc
2
.envrc
|
@ -1,2 +1,2 @@
|
||||||
export DATABASE_URL="sqlite:lib/crossfire.sqlite"
|
export DATABASE_URL="sqlite:lib/crossfire.sqlite"
|
||||||
[ ! -d lib ] && mkdir lib
|
[ -d lib ] || mkdir -p lib/projects
|
||||||
|
|
|
@ -13,7 +13,8 @@ create table node_resource(nodeid integer not null, resource text not null,
|
||||||
foreign key(nodeid) references node(id));
|
foreign key(nodeid) references node(id));
|
||||||
-- }
|
-- }
|
||||||
-- @up {
|
-- @up {
|
||||||
create table task(id integer primary key, name text not null, manifest blob not null);
|
create table task(id integer primary key, name text not null, manifest blob not null,
|
||||||
|
committed boolean not null);
|
||||||
-- }
|
-- }
|
||||||
-- @up {
|
-- @up {
|
||||||
create table task_log(taskid integer not null, worker integer not null,
|
create table task_log(taskid integer not null, worker integer not null,
|
||||||
|
|
|
@ -20,7 +20,9 @@
|
||||||
racket/bool racket/fasl racket/file racket/match racket/runtime-path racket/path
|
racket/bool racket/fasl racket/file racket/match racket/runtime-path racket/path
|
||||||
racket/set racket/string
|
racket/set racket/string
|
||||||
north/base north/adapter/base north/adapter/sqlite
|
north/base north/adapter/base north/adapter/sqlite
|
||||||
"comms.rkt" "not-crypto.rkt")
|
"comms.rkt" "not-crypto.rkt"
|
||||||
|
;; port-fsync
|
||||||
|
(submod "static-support.rkt" misc-calls))
|
||||||
|
|
||||||
;; configuration
|
;; configuration
|
||||||
|
|
||||||
|
@ -28,6 +30,7 @@
|
||||||
|
|
||||||
(define SERVER-DATA-DIR (if PRODUCTION? "/var/lib/crossfire/" "lib/"))
|
(define SERVER-DATA-DIR (if PRODUCTION? "/var/lib/crossfire/" "lib/"))
|
||||||
(define SERVER-DB-PATH (build-path SERVER-DATA-DIR "crossfire.sqlite"))
|
(define SERVER-DB-PATH (build-path SERVER-DATA-DIR "crossfire.sqlite"))
|
||||||
|
(define SERVER-FILES-PATH (build-path SERVER-DATA-DIR "projects/"))
|
||||||
(define AGENT-ARCH-PREFIX "arch_")
|
(define AGENT-ARCH-PREFIX "arch_")
|
||||||
(define AGENT-BINARY "crossfire-agent")
|
(define AGENT-BINARY "crossfire-agent")
|
||||||
|
|
||||||
|
@ -77,11 +80,61 @@
|
||||||
(define-stmt q-get-node-type "select type from node where id=?")
|
(define-stmt q-get-node-type "select type from node where id=?")
|
||||||
(define-stmt q-get-node-info "select name, arch, type, secret from node where id=?")
|
(define-stmt q-get-node-info "select name, arch, type, secret from node where id=?")
|
||||||
|
|
||||||
|
(define-stmt q-new-task "insert into task (name, manifest, committed) values (?, ?, 0)")
|
||||||
|
(define-stmt q-get-task-id-commit "select id, committed from task")
|
||||||
|
(define-stmt q-set-task-commit "update task set committed=1 where id=?")
|
||||||
|
(define-stmt q-delete-task "delete from task where id=?")
|
||||||
|
(define-stmt q-get-tasks "select id, name, manifest from task")
|
||||||
|
|
||||||
|
;; utils
|
||||||
|
|
||||||
|
(define (query/insert-id db stmt . args)
|
||||||
|
(define info (simple-result-info (apply query db stmt args)))
|
||||||
|
(cdr (assoc 'insert-id info)))
|
||||||
|
|
||||||
|
;; cleanly recovers from potential crash situations
|
||||||
|
(define (server-cleanup-unused-files)
|
||||||
|
(define existing-ids (mutable-set))
|
||||||
|
(call-with-transaction (current-db) (lambda ()
|
||||||
|
(define (cleanup id exists? path)
|
||||||
|
(displayln (format "removing corrupted/incomplete task ~a" id))
|
||||||
|
(when exists? (delete-file path))
|
||||||
|
(query-exec (current-db) q-delete-task id))
|
||||||
|
(for ([(id committed) (in-query (current-db) q-get-task-id-commit)])
|
||||||
|
(set-add! existing-ids (number->string id))
|
||||||
|
(define path (build-path SERVER-FILES-PATH (number->string id)))
|
||||||
|
(define exists? (file-exists? path))
|
||||||
|
(define committed? (= 1 committed))
|
||||||
|
(cond
|
||||||
|
;; potentially crashed while file was fsync'd but the directory was not
|
||||||
|
[(and committed? (not exists?)) (cleanup id exists? path)]
|
||||||
|
;; crashed between row insert and file fsync
|
||||||
|
[(not committed?) (cleanup id exists? path)]
|
||||||
|
[else (void)]))))
|
||||||
|
;; delete any unaffiliated files
|
||||||
|
(for ([subpath (in-directory SERVER-FILES-PATH)])
|
||||||
|
(define name (path->string (file-name-from-path subpath)))
|
||||||
|
(unless (set-member? existing-ids name)
|
||||||
|
(delete-file subpath)))
|
||||||
|
(void))
|
||||||
|
|
||||||
|
;; commits a file corresponding to the task
|
||||||
|
(define (server-commit-file taskid data)
|
||||||
|
(define path (build-path SERVER-FILES-PATH (number->string taskid)))
|
||||||
|
(call-with-output-file path
|
||||||
|
(lambda (out)
|
||||||
|
(write-bytes data out)
|
||||||
|
(port-fsync out))
|
||||||
|
#:mode 'binary
|
||||||
|
#:exists 'truncate)
|
||||||
|
(query-exec (current-db) q-set-task-commit taskid))
|
||||||
|
|
||||||
;; rpc calls
|
;; rpc calls
|
||||||
|
|
||||||
(define-rpc-type server)
|
(define-rpc-type server)
|
||||||
|
|
||||||
(struct node-info [id name arch type resources online?] #:transparent)
|
(struct node-info [id name arch type resources online?] #:prefab)
|
||||||
|
(struct project-info [id name manifest] #:prefab)
|
||||||
|
|
||||||
(define (get-nodes type)
|
(define (get-nodes type)
|
||||||
(define type-str (symbol->string type))
|
(define type-str (symbol->string type))
|
||||||
|
@ -95,9 +148,7 @@
|
||||||
(call-with-transaction (current-db) (lambda ()
|
(call-with-transaction (current-db) (lambda ()
|
||||||
(define secret (crypto-sign-make-key))
|
(define secret (crypto-sign-make-key))
|
||||||
(define public (crypto-sign-public-key secret))
|
(define public (crypto-sign-public-key secret))
|
||||||
(define info
|
(define id (query/insert-id (current-db) q-new-node name arch (symbol->string type) secret))
|
||||||
(simple-result-info (query (current-db) q-new-node name arch (symbol->string type) secret)))
|
|
||||||
(define id (cdr (assoc 'insert-id info)))
|
|
||||||
(for ([res (in-list resources)])
|
(for ([res (in-list resources)])
|
||||||
(query-exec (current-db) q-add-node-res id res))
|
(query-exec (current-db) q-add-node-res id res))
|
||||||
(values id public))))
|
(values id public))))
|
||||||
|
@ -118,6 +169,12 @@
|
||||||
[(list _ ... "linux" "gnu") (configure.linux-gnu)]
|
[(list _ ... "linux" "gnu") (configure.linux-gnu)]
|
||||||
[_ (error "XXX: don't know how to configure arch" agent-arch)]))
|
[_ (error "XXX: don't know how to configure arch" agent-arch)]))
|
||||||
|
|
||||||
|
(define (make-task name manifest tar)
|
||||||
|
(define manifest-data (s-exp->fasl manifest))
|
||||||
|
(define id (query/insert-id (current-db) q-new-task name manifest-data))
|
||||||
|
(server-commit-file id tar)
|
||||||
|
id)
|
||||||
|
|
||||||
(define (enforce-subject type)
|
(define (enforce-subject type)
|
||||||
(unless (symbol=? type (node-type (current-from-node)))
|
(unless (symbol=? type (node-type (current-from-node)))
|
||||||
(error "unauthorized")))
|
(error "unauthorized")))
|
||||||
|
@ -161,6 +218,17 @@
|
||||||
arch (current-server-public-node))]
|
arch (current-server-public-node))]
|
||||||
[_ (error "invalid id or wrong node type")]))
|
[_ (error "invalid id or wrong node type")]))
|
||||||
|
|
||||||
|
;; client rpcs :: projects
|
||||||
|
|
||||||
|
(define-rpc server (new-project name manifest tar)
|
||||||
|
(enforce-subject 'client)
|
||||||
|
(make-task name manifest tar))
|
||||||
|
|
||||||
|
(define-rpc server (get-projects)
|
||||||
|
(enforce-subject 'client)
|
||||||
|
(for/list ([(id name manifest) (in-query (current-db) q-get-tasks)])
|
||||||
|
(project-info id name (fasl->s-exp manifest))))
|
||||||
|
|
||||||
;; agent rpcs
|
;; agent rpcs
|
||||||
|
|
||||||
(define-rpc server (agent-report something)
|
(define-rpc server (agent-report something)
|
||||||
|
@ -173,6 +241,11 @@
|
||||||
(require racket/cmdline)
|
(require racket/cmdline)
|
||||||
(current-db (open-server-db 'create))
|
(current-db (open-server-db 'create))
|
||||||
(migrate-server-db)
|
(migrate-server-db)
|
||||||
|
|
||||||
|
(parameterize ([current-from-node (node 100 "meow" 'client #f #f #f #f)])
|
||||||
|
((rpc-impl server get-projects)))
|
||||||
|
|
||||||
|
; (make-task "meow-task" '((meow . 10)) #"this is some extra data")
|
||||||
; (define data (configure-agent-binary (node 10 "meow0" 'agent #f #f #f #f)
|
; (define data (configure-agent-binary (node 10 "meow0" 'agent #f #f #f #f)
|
||||||
; "aarch64-unknown-linux-gnu"
|
; "aarch64-unknown-linux-gnu"
|
||||||
; (node 0 "server" 'server #f #f "meow.systems" 1337)))
|
; (node 0 "server" 'server #f #f "meow.systems" 1337)))
|
||||||
|
|
|
@ -82,3 +82,32 @@
|
||||||
(error "static ffi entry not found" name))
|
(error "static ffi entry not found" name))
|
||||||
|
|
||||||
(cast ent _fun-opaque type))
|
(cast ent _fun-opaque type))
|
||||||
|
|
||||||
|
;; misc ffi calls not provided by racket (because ????)
|
||||||
|
;; racket should have fsync like cmon
|
||||||
|
(module+ misc-calls
|
||||||
|
(require ffi/unsafe/port racket/match)
|
||||||
|
|
||||||
|
(provide port-fsync)
|
||||||
|
|
||||||
|
(define port-fsync.unix
|
||||||
|
;; lazy time
|
||||||
|
(let ([fsync-call #f])
|
||||||
|
(lambda (port)
|
||||||
|
(define fd (unsafe-port->file-descriptor port))
|
||||||
|
(when (false? fd) (error "invalid port provided"))
|
||||||
|
(when (false? fsync-call)
|
||||||
|
(set! fsync-call
|
||||||
|
(get-ffi-obj/runtime
|
||||||
|
"fsync" (ffi-lib/runtime "libc" '("6" "7"))
|
||||||
|
(_fun #:save-errno 'posix _int -> (res : _int)
|
||||||
|
-> (unless (zero? res)
|
||||||
|
(raise (exn:fail:filesystem:errno (format "fsync: errno ~a" (saved-errno))
|
||||||
|
(current-continuation-marks)
|
||||||
|
(cons (saved-errno) 'posix))))))))
|
||||||
|
(fsync-call fd))))
|
||||||
|
|
||||||
|
(define (port-fsync port)
|
||||||
|
(match (system-type 'os)
|
||||||
|
['unix (port-fsync.unix port)]
|
||||||
|
[x (error "don't know how to fsync on" x)])))
|
||||||
|
|
Loading…
Reference in New Issue