mirror of
https://git.in.rschanz.org/ryan77627/guix.git
synced 2024-12-24 05:18:07 -05:00
services: postgresql: Add postgresql-role-service-type.
* gnu/services/databases.scm (postgresql-role, postgresql-role?, postgresql-role-name, postgresql-role-permissions, postgresql-role-create-database?, postgresql-role-configuration, postgresql-role-configuration?, postgresql-role-configuration-host, postgresql-role-configuration-roles, postgresql-role-service-type): New procedures. * gnu/tests/databases.scm: Test it. * doc/guix.texi: Document it.
This commit is contained in:
parent
33687aa3d0
commit
ec145a2ff9
3 changed files with 207 additions and 1 deletions
|
@ -19455,6 +19455,68 @@ here}.
|
|||
@end table
|
||||
@end deftp
|
||||
|
||||
@deffn {Scheme Variable} postgresql-role-service-type
|
||||
This service allows to create PostgreSQL roles and databases after
|
||||
PostgreSQL service start. Here is an example of its use.
|
||||
|
||||
@lisp
|
||||
(service postgresql-role-service-type
|
||||
(postgresql-role-configuration
|
||||
(roles
|
||||
(list (postgresql-role
|
||||
(name "test")
|
||||
(create-database? #t))))))
|
||||
@end lisp
|
||||
|
||||
This service can be extended with extra roles, as in this
|
||||
example:
|
||||
|
||||
@lisp
|
||||
(service-extension postgresql-role-service-type
|
||||
(const (postgresql-role
|
||||
(name "alice")
|
||||
(create-database? #t))))
|
||||
@end lisp
|
||||
@end deffn
|
||||
|
||||
@deftp {Data Type} postgresql-role
|
||||
PostgreSQL manages database access permissions using the concept of
|
||||
roles. A role can be thought of as either a database user, or a group
|
||||
of database users, depending on how the role is set up. Roles can own
|
||||
database objects (for example, tables) and can assign privileges on
|
||||
those objects to other roles to control who has access to which objects.
|
||||
|
||||
@table @asis
|
||||
@item @code{name}
|
||||
The role name.
|
||||
|
||||
@item @code{permissions} (default: @code{'(createdb login)})
|
||||
The role permissions list. Supported permissions are @code{bypassrls},
|
||||
@code{createdb}, @code{createrole}, @code{login}, @code{replication} and
|
||||
@code{superuser}.
|
||||
|
||||
@item @code{create-database?} (default: @code{#f})
|
||||
Whether to create a database with the same name as the role.
|
||||
|
||||
@end table
|
||||
@end deftp
|
||||
|
||||
@deftp {Data Type} postgresql-role-configuration
|
||||
Data type representing the configuration of
|
||||
@var{postgresql-role-service-type}.
|
||||
|
||||
@table @asis
|
||||
@item @code{host} (default: @code{"/var/run/postgresql"})
|
||||
The PostgreSQL host to connect to.
|
||||
|
||||
@item @code{log} (default: @code{"/var/log/postgresql_roles.log"})
|
||||
File name of the log file.
|
||||
|
||||
@item @code{roles} (default: @code{'()})
|
||||
The initial PostgreSQL roles to create.
|
||||
@end table
|
||||
@end deftp
|
||||
|
||||
@subsubheading MariaDB/MySQL
|
||||
|
||||
@defvr {Scheme Variable} mysql-service-type
|
||||
|
|
|
@ -58,6 +58,18 @@ (define-module (gnu services databases)
|
|||
postgresql-service
|
||||
postgresql-service-type
|
||||
|
||||
postgresql-role
|
||||
postgresql-role?
|
||||
postgresql-role-name
|
||||
postgresql-role-permissions
|
||||
postgresql-role-create-database?
|
||||
postgresql-role-configuration
|
||||
postgresql-role-configuration?
|
||||
postgresql-role-configuration-host
|
||||
postgresql-role-configuration-roles
|
||||
|
||||
postgresql-role-service-type
|
||||
|
||||
memcached-service-type
|
||||
memcached-configuration
|
||||
memcached-configuration?
|
||||
|
@ -343,6 +355,96 @@ (define-deprecated (postgresql-service #:key (postgresql postgresql)
|
|||
(data-directory data-directory)
|
||||
(extension-packages extension-packages))))
|
||||
|
||||
(define-record-type* <postgresql-role>
|
||||
postgresql-role make-postgresql-role
|
||||
postgresql-role?
|
||||
(name postgresql-role-name) ;string
|
||||
(permissions postgresql-role-permissions
|
||||
(default '(createdb login))) ;list
|
||||
(create-database? postgresql-role-create-database? ;boolean
|
||||
(default #f)))
|
||||
|
||||
(define-record-type* <postgresql-role-configuration>
|
||||
postgresql-role-configuration make-postgresql-role-configuration
|
||||
postgresql-role-configuration?
|
||||
(host postgresql-role-configuration-host ;string
|
||||
(default "/var/run/postgresql"))
|
||||
(log postgresql-role-configuration-log ;string
|
||||
(default "/var/log/postgresql_roles.log"))
|
||||
(roles postgresql-role-configuration-roles
|
||||
(default '()))) ;list
|
||||
|
||||
(define (postgresql-create-roles config)
|
||||
;; See: https://www.postgresql.org/docs/current/sql-createrole.html for the
|
||||
;; complete permissions list.
|
||||
(define (format-permissions permissions)
|
||||
(let ((dict '(bypassrls createdb createrole login replication superuser)))
|
||||
(string-join (filter-map (lambda (permission)
|
||||
(and (member permission dict)
|
||||
(string-upcase
|
||||
(symbol->string permission))))
|
||||
permissions)
|
||||
" ")))
|
||||
|
||||
(define (roles->queries roles)
|
||||
(apply mixed-text-file "queries"
|
||||
(append-map
|
||||
(lambda (role)
|
||||
(match-record role <postgresql-role>
|
||||
(name permissions create-database?)
|
||||
`("SELECT NOT(EXISTS(SELECT 1 FROM pg_catalog.pg_roles WHERE \
|
||||
rolname = '" ,name "')) as not_exists;\n"
|
||||
"\\gset\n"
|
||||
"\\if :not_exists\n"
|
||||
"CREATE ROLE " ,name
|
||||
" WITH " ,(format-permissions permissions)
|
||||
";\n"
|
||||
,@(if create-database?
|
||||
`("CREATE DATABASE " ,name
|
||||
" OWNER " ,name ";\n")
|
||||
'())
|
||||
"\\endif\n")))
|
||||
roles)))
|
||||
|
||||
(let ((host (postgresql-role-configuration-host config))
|
||||
(roles (postgresql-role-configuration-roles config)))
|
||||
(program-file
|
||||
"postgresql-create-roles"
|
||||
#~(begin
|
||||
(let ((psql #$(file-append postgresql "/bin/psql")))
|
||||
(execl psql psql "-a"
|
||||
"-h" #$host
|
||||
"-f" #$(roles->queries roles)))))))
|
||||
|
||||
(define (postgresql-role-shepherd-service config)
|
||||
(match-record config <postgresql-role-configuration>
|
||||
(log)
|
||||
(list (shepherd-service
|
||||
(requirement '(postgres))
|
||||
(provision '(postgres-roles))
|
||||
(one-shot? #t)
|
||||
(start #~(make-forkexec-constructor
|
||||
(list #$(postgresql-create-roles config))
|
||||
#:user "postgres" #:group "postgres"
|
||||
#:log-file #$log))
|
||||
(documentation "Create PostgreSQL roles.")))))
|
||||
|
||||
(define postgresql-role-service-type
|
||||
(service-type (name 'postgresql-role)
|
||||
(extensions
|
||||
(list (service-extension shepherd-root-service-type
|
||||
postgresql-role-shepherd-service)))
|
||||
(compose concatenate)
|
||||
(extend (lambda (config extended-roles)
|
||||
(match-record config <postgresql-role-configuration>
|
||||
(host roles)
|
||||
(postgresql-role-configuration
|
||||
(host host)
|
||||
(roles (append roles extended-roles))))))
|
||||
(default-value (postgresql-role-configuration))
|
||||
(description "Ensure the specified PostgreSQL roles are
|
||||
created after the PostgreSQL database is started.")))
|
||||
|
||||
|
||||
;;;
|
||||
;;; Memcached
|
||||
|
|
|
@ -217,6 +217,9 @@ (define %test-mongodb
|
|||
(define %postgresql-log-directory
|
||||
"/var/log/postgresql")
|
||||
|
||||
(define %role-log-file
|
||||
"/var/log/postgresql_roles.log")
|
||||
|
||||
(define %postgresql-os
|
||||
(simple-operating-system
|
||||
(service postgresql-service-type
|
||||
|
@ -229,7 +232,13 @@ (define %postgresql-os
|
|||
("random_page_cost" 2)
|
||||
("auto_explain.log_min_duration" "100 ms")
|
||||
("work_mem" "500 MB")
|
||||
("debug_print_plan" #t)))))))))
|
||||
("debug_print_plan" #t)))))))
|
||||
(service postgresql-role-service-type
|
||||
(postgresql-role-configuration
|
||||
(roles
|
||||
(list (postgresql-role
|
||||
(name "root")
|
||||
(create-database? #t))))))))
|
||||
|
||||
(define (run-postgresql-test)
|
||||
"Run tests in %POSTGRESQL-OS."
|
||||
|
@ -282,6 +291,39 @@ (define marionette
|
|||
#t))
|
||||
marionette))
|
||||
|
||||
(test-assert "database ready"
|
||||
(begin
|
||||
(marionette-eval
|
||||
'(begin
|
||||
(let loop ((i 10))
|
||||
(unless (or (zero? i)
|
||||
(and (file-exists? #$%role-log-file)
|
||||
(string-contains
|
||||
(call-with-input-file #$%role-log-file
|
||||
get-string-all)
|
||||
";\nCREATE DATABASE")))
|
||||
(sleep 1)
|
||||
(loop (- i 1)))))
|
||||
marionette)))
|
||||
|
||||
(test-assert "database creation"
|
||||
(marionette-eval
|
||||
'(begin
|
||||
(use-modules (gnu services herd)
|
||||
(ice-9 popen))
|
||||
(current-output-port
|
||||
(open-file "/dev/console" "w0"))
|
||||
(let* ((port (open-pipe*
|
||||
OPEN_READ
|
||||
#$(file-append postgresql "/bin/psql")
|
||||
"-tAh" "/var/run/postgresql"
|
||||
"-c" "SELECT 1 FROM pg_database WHERE
|
||||
datname='root'"))
|
||||
(output (get-string-all port)))
|
||||
(close-pipe port)
|
||||
(string-contains output "1")))
|
||||
marionette))
|
||||
|
||||
(test-end)
|
||||
(exit (= (test-runner-fail-count (test-runner-current)) 0)))))
|
||||
|
||||
|
|
Loading…
Reference in a new issue