Basic playbook functionality

This commit is contained in:
Ray Miller 2025-06-01 15:50:12 +01:00
parent 3685369de5
commit 38f08e8ce4
Signed by: ray
GPG key ID: 043F786C4CD681B8
8 changed files with 237 additions and 33 deletions

19
examples/inventory.scm Normal file
View file

@ -0,0 +1,19 @@
(use-modules (ordo connection)
(ordo inventory))
(list
(host #:name "localhost"
#:connection (local-connection)
#:tags '(#:linux #:guix))
(host #:name "limiting-factor"
#:connection (ssh-connection "limiting-factor" #:user "core")
#:tags '(#:linux #:coreos))
(host #:name "screw-loose"
#:connection (ssh-connection "screw-loose" #:user "core")
#:tags '(#:linux #:coreos))
(host #:name "control-surface"
#:connection (ssh-connection "control-surface")
#:tags '(#:linux #:debian)))

8
examples/playbook.scm Normal file
View file

@ -0,0 +1,8 @@
(use-modules (ordo playbook))
(playbook
#:name "Example playbook"
#:vars '((foo . 1) (bar . "baz"))
#:plays (list
(play #:name "Example play"
#:host "localhost")))

View file

@ -1,8 +1,12 @@
(define-module (ordo cli run)
#:use-module (config)
#:use-module (config api)
#:use-module (ice-9 filesystem)
#:use-module (ordo inventory)
#:use-module (ordo logger)
#:use-module (ordo playbook)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-26)
#:export (config handler))
(define (valid-tags? x)
@ -21,9 +25,9 @@
(list
(setting
(name 'inventory)
(default "inventory.scm")
(default "/dev/null")
(example "examples/inventory.scm")
(handler identity)
(handler (cut expand-file-name <> #f #t))
(test file-exists?)
(synopsis "Inventory file"))
(switch
@ -37,12 +41,11 @@
(list
(argument
(name 'playbook)
(handler identity)
(handler (cut expand-file-name <> #f #t))
(test file-exists?))))
(synopsis "Run a playbook")))
(define (handler options)
(let ((inventory (option-ref options 'inventory))
(playbook (option-ref options '(playbook))))
(log-msg 'INFO "Running playbook " playbook " with inventory " inventory)
))
(let ((inventory (load-inventory (option-ref options 'inventory)))
(playbook (load-playbook (option-ref options '(playbook)))))
(run-playbook playbook inventory)))

View file

@ -23,23 +23,25 @@
(define (local-connection)
(make <local-connection>))
(define* (ssh-connection user host #:key (password #f) (identity #f) (authenticate-server? #t))
(define* (ssh-connection host #:key (user (getlogin)) (password #f) (identity #f) (authenticate-server? #t))
(make <ssh-connection> #:user user #:host host #:password password
#:identity identity #:authenticate-server? authenticate-server?))
(define* (call-with-connection conn proc #:key sudo? sudo-user sudo-password)
(when (and sudo? (not (is-a? conn <sudo-connection>)))
(raise-exception
(make-exception
(make-programming-error)
(make-exception-with-message (format #f "connection ~a does not support sudo" conn)))))
(set! (become? conn) sudo?)
(set! (become-user conn) sudo-user)
(set! (become-password conn) sudo-password)
(dynamic-wind
(lambda () (setup conn))
(lambda () (proc conn))
(lambda () (teardown conn))))
(let ((conn (deep-clone conn)))
(when sudo?
(unless (is-a? conn <sudo-connection>)
(raise-exception
(make-exception
(make-programming-error)
(make-exception-with-message (format #f "connection ~a does not support sudo" conn)))))
(set! (become? conn) sudo?)
(set! (become-user conn) sudo-user)
(set! (become-password conn) sudo-password))
(dynamic-wind
(lambda () (setup conn))
(lambda () (proc conn))
(lambda () (teardown conn)))))
(define (run conn prog . args)
(let* ((args options (break keyword? args))

49
ordo/context.scm Normal file
View file

@ -0,0 +1,49 @@
(define-module (ordo context)
#:use-module (srfi srfi-69))
;;
;; Inventory
;;
(define-public *inventory* (make-parameter #f))
;;
;; Playbook vars
;;
(define-public *playbook-vars* (make-parameter #f))
(define-public (playbook-var-ref key)
(hash-table-ref (*playbook-vars*) key))
(define-public (playbook-var-ref/default key default)
(hash-table-ref/default (*playbook-vars*) key default))
(define-public (playbook-var-set! key value)
(hash-table-set! (*playbook-vars*) key value))
;;
;; Play vars
;;
(define-public *play-vars* (make-parameter #f))
(define-public (play-var-ref key)
(hash-table-ref (*play-vars*) key))
(define-public (play-var-ref/default key default)
(hash-table-ref/default (*play-vars*) key default))
(define-public (play-var-set! key value)
(hash-table-set! (*play-vars*) key value))
;;
;; Host vars
;;
(define-public *host-vars* (make-parameter #f))
(define-public (host-var-ref key)
(hash-table-ref (*host-vars*) key))
(define-public (host-var-ref/default key default)
(hash-table-ref/default (*host-vars*) key default))
(define-public (host-var-set! key value)
(hash-table-set! (*host-vars*) key value))

View file

@ -1,25 +1,37 @@
(define-module (ordo inventory)
#:use-module (ice-9 eval-string)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-9)
#:use-module (ice-9 textual-ports)
#:use-module (oop goops)
#:use-module ((ordo connection) #:select (local-connection))
#:use-module (ordo logger)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-69)
#:export (host
host?
host-name
host-connection
host-tags
add-host!
resolve-hosts))
host-vars
resolve-hosts
load-inventory))
(define-record-type <host>
(make-host name connection tags)
host?
(name host-name)
(connection host-connection)
(tags host-tags))
(define-class <host> ()
(name #:init-keyword #:name #:getter host-name)
(connection #:init-keyword #:connection #:getter host-connection)
(tags #:init-keyword #:tags #:getter host-tags #:init-form (list))
(vars #:init-keyword #:vars #:getter host-vars #:init-form (list)))
(define (host name connection . tags)
(make-host name connection tags))
(define-method (initialize (object <host>) initargs)
(next-method)
(slot-set! object 'vars (alist->hash-table (slot-ref object 'vars)))
object)
(define (host . args)
(apply make <host> args))
(define (host? x)
(is-a? x <host>))
(define (tagged-every? wanted-tags)
(lambda (h)
@ -36,9 +48,18 @@
(define (resolve-hosts inventory expr)
(match expr
("localhost" (list (or (find (named? "localhost") inventory)
(make-host "localhost" (local-connection) '()))))
(make <host> #:name "localhost" #:connection (local-connection)))))
((? string? hostname) (filter (named? hostname) inventory))
('all inventory)
(('tagged tag) (filter (tagged-every? (list tag)) inventory))
(('tagged/every tag . tags) (filter (tagged-every? (cons tag tags)) inventory))
(('tagged/any tag . tags) (filter (tagged-any? (cons tag tags)) inventory))))
(define (load-inventory filename)
(log-msg 'INFO "Loading inventory " filename)
(let* ((inventory (eval-string (call-with-input-file filename get-string-all)
#:file filename))
(inventory (if (list? inventory) inventory '())))
(when (null? inventory)
(log-msg 'NOTICE "Inventory is empty, only localhost will be available"))
inventory))

57
ordo/play.scm Normal file
View file

@ -0,0 +1,57 @@
(define-module (ordo play)
#:use-module (oop goops)
#:use-module (ordo connection)
#:use-module (ordo context)
#:use-module (ordo inventory)
#:use-module (ordo logger)
#:use-module (ordo util flatten)
#:use-module (srfi srfi-26)
#:use-module (srfi srfi-69)
#:export (play
play?
play-host
play-sudo?
play-sudo-user
play-sudo-password
play-vars
play-actions
play-handlers
run-play))
(define-class <play> ()
(name #:init-keyword #:name #:getter play-name)
(host #:init-keyword #:host #:getter play-host)
(sudo? #:init-keyword #:sudo? #:getter play-sudo? #:init-value #f)
(sudo-user #:init-keyword #:sudo-user #:getter play-sudo-user #:init-value #f)
(sudo-password #:init-keyword #:sudo-password #:getter play-sudo-password #:init-value #f)
(vars #:init-keyword #:vars #:getter play-vars #:init-form (list))
(actions #:init-keyword #:actions #:getter play-actions #:init-form (list))
(handlers #:init-keyword #:handlers #:getter play-handlers #:init-form (list)))
(define-method (initialize (object <play>) initargs)
(next-method)
(slot-set! object 'vars (alist->hash-table (slot-ref object 'vars)))
object)
(define (play . args)
(apply make <play> args))
(define (run-play p)
(log-msg 'NOTICE "Running play: " (play-name p))
(parameterize ((*play-vars* (play-vars p)))
(let ((hosts (resolve-hosts (*inventory*) (play-host p))))
(if (null? hosts)
(log-msg 'WARN "No hosts matched: " (play-host p))
(for-each (lambda (h) (run-host-play p h)) hosts)))))
(define (run-host-play p h)
(log-msg 'NOTICE "Running play: " (play-name p) " on host: " (host-name h))
(parameterize ((*host-vars* (host-vars h)))
(call-with-connection
(host-connection h)
(lambda (conn)
#f
)
#:sudo? (play-sudo? p)
#:sudo-user (play-sudo-user p)
#:sudo-password (play-sudo-password p))))

45
ordo/playbook.scm Normal file
View file

@ -0,0 +1,45 @@
(define-module (ordo playbook)
#:use-module (ice-9 eval-string)
#:use-module (ice-9 textual-ports)
#:use-module (oop goops)
#:use-module (ordo context)
#:use-module (ordo logger)
#:use-module (ordo play)
#:use-module (srfi srfi-26)
#:use-module (srfi srfi-69)
#:export (<playbook>
playbook
playbook?
playbook-name
playbook-vars
playbook-plays
load-playbook
run-playbook)
#:re-export (play))
(define-class <playbook> ()
(name #:init-keyword #:name #:getter playbook-name)
(vars #:init-keyword #:vars #:getter playbook-vars)
(plays #:init-keyword #:plays #:getter playbook-plays))
(define-method (initialize (object <playbook>) initargs)
(next-method)
(slot-set! object 'vars (alist->hash-table (slot-ref object 'vars)))
object)
(define (playbook . args)
(apply make <playbook> args))
(define (playbook? p)
(is-a? p <playbook>))
(define (load-playbook filename)
(log-msg 'INFO "Loading playbook " filename)
(eval-string (call-with-input-file filename get-string-all)
#:file filename))
(define (run-playbook pb inventory)
(log-msg 'NOTICE "Running playbook: " (playbook-name pb))
(parameterize ((*inventory* inventory)
(*playbook-vars* (playbook-vars pb)))
(for-each run-play (playbook-plays pb))))