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

View file

@ -23,23 +23,25 @@
(define (local-connection) (define (local-connection)
(make <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 (make <ssh-connection> #:user user #:host host #:password password
#:identity identity #:authenticate-server? authenticate-server?)) #:identity identity #:authenticate-server? authenticate-server?))
(define* (call-with-connection conn proc #:key sudo? sudo-user sudo-password) (define* (call-with-connection conn proc #:key sudo? sudo-user sudo-password)
(when (and sudo? (not (is-a? conn <sudo-connection>))) (let ((conn (deep-clone conn)))
(raise-exception (when sudo?
(make-exception (unless (is-a? conn <sudo-connection>)
(make-programming-error) (raise-exception
(make-exception-with-message (format #f "connection ~a does not support sudo" conn))))) (make-exception
(set! (become? conn) sudo?) (make-programming-error)
(set! (become-user conn) sudo-user) (make-exception-with-message (format #f "connection ~a does not support sudo" conn)))))
(set! (become-password conn) sudo-password) (set! (become? conn) sudo?)
(dynamic-wind (set! (become-user conn) sudo-user)
(lambda () (setup conn)) (set! (become-password conn) sudo-password))
(lambda () (proc conn)) (dynamic-wind
(lambda () (teardown conn)))) (lambda () (setup conn))
(lambda () (proc conn))
(lambda () (teardown conn)))))
(define (run conn prog . args) (define (run conn prog . args)
(let* ((args options (break keyword? 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) (define-module (ordo inventory)
#:use-module (ice-9 eval-string)
#:use-module (ice-9 match) #:use-module (ice-9 match)
#:use-module (srfi srfi-1) #:use-module (ice-9 textual-ports)
#:use-module (srfi srfi-9) #:use-module (oop goops)
#:use-module ((ordo connection) #:select (local-connection)) #:use-module ((ordo connection) #:select (local-connection))
#:use-module (ordo logger)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-69)
#:export (host #:export (host
host? host?
host-name host-name
host-connection host-connection
host-tags host-tags
add-host! host-vars
resolve-hosts)) resolve-hosts
load-inventory))
(define-record-type <host> (define-class <host> ()
(make-host name connection tags) (name #:init-keyword #:name #:getter host-name)
host? (connection #:init-keyword #:connection #:getter host-connection)
(name host-name) (tags #:init-keyword #:tags #:getter host-tags #:init-form (list))
(connection host-connection) (vars #:init-keyword #:vars #:getter host-vars #:init-form (list)))
(tags host-tags))
(define (host name connection . tags) (define-method (initialize (object <host>) initargs)
(make-host name connection tags)) (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) (define (tagged-every? wanted-tags)
(lambda (h) (lambda (h)
@ -36,9 +48,18 @@
(define (resolve-hosts inventory expr) (define (resolve-hosts inventory expr)
(match expr (match expr
("localhost" (list (or (find (named? "localhost") inventory) ("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)) ((? string? hostname) (filter (named? hostname) inventory))
('all inventory) ('all inventory)
(('tagged tag) (filter (tagged-every? (list tag)) inventory)) (('tagged tag) (filter (tagged-every? (list tag)) inventory))
(('tagged/every tag . tags) (filter (tagged-every? (cons tag tags)) inventory)) (('tagged/every tag . tags) (filter (tagged-every? (cons tag tags)) inventory))
(('tagged/any tag . tags) (filter (tagged-any? (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))))