Updating service definitions format.

THIS IS A BREAKING CHANGE

Hopefully, this change will make it easier to remove data on service
removal, but also to export and backup service data and to add
per-service system users.

A few obsolete service definitions have also been updated to the new
format.

Testing is still incomplete, you may want to wait a few commits before
using this.
master
Luka Vandervelden 2019-11-08 14:36:59 +01:00
parent e0e81d8581
commit 87929be026
10 changed files with 133 additions and 116 deletions

View File

@ -1,22 +1,17 @@
name: authd
command: authd -K ${SERVICE_ROOT}/jwt_key -u ${SERVICE_ROOT}/passwd -g ${SERVICE_ROOT}/group
environment-variables:
- LD_LIBRARY_PATH=/usr/local/lib
command: authd -K jwt_key -u passwd -g group
user: authd
provides: auth
%pre-start
name: Creating IPC directory
unless-directory: /run/ipc
%file /run/ipc
name: IPC directory
command: install -d -m6777 /run/ipc
%pre-start
name: Creating JWT key
unless-file: ${SERVICE_ROOT}/jwt_key
command: head -c 64 /dev/urandom | base64 > ${SERVICE_ROOT}/jwt_key
%pre-start
%file jwt_key
name: JWT key
creation-command: head -c 64 /dev/urandom | base64 > jwt_key
%file passwd
name: passwd file
unless-file: ${SERVICE_ROOT}/passwd
command: touch ${SERVICE_ROOT}/passwd
%pre-start
command: touch passwd
%file group
name: group file
unless-file: ${SERVICE_ROOT}/group
command: touch ${SERVICE_ROOT}/group
command: touch group

View File

@ -1,15 +1,23 @@
command: gitea -C . -w . -c ./custom/conf/app.ini
command: gitea -C . -w . -c gitea.cfg
consumes: postgresql, http?
requires-domain: true
ports: http
%directory ${SERVICE_ROOT}/custom/conf
name: working directory
#%directory ${SERVICE_ROOT}/custom/conf
# name: working directory
# configuration: true
%configuration gitea.cfg ${SERVICE_ROOT}/custom/conf/app.ini
%pre-start
name: gitea database creation
%file db-is-setup
name: postgresql database
# 'command' is run only if this directory doesn't exist
unless-file: ${SERVICE_ROOT}/db_is_setup
command: pg_create_user.sh create_user_and_db && touch ${SERVICE_ROOT}/db_is_setup
creation-command: pg_create_user.sh create_user_and_db && touch db-is-setup
deletion-command: pg_remove_user.sh bla bla bla && rm db-is-setup
export-command: pg_export_user.sh bla bla bla # FIXME: Where/how does it export?
# With syntaxic sugar.
%configuration gitea.cfg
#%database
# type: postgresql
# from-token: postgresql

8
services/kanban.spec Normal file
View File

@ -0,0 +1,8 @@
name: kanband
command: kanband -k ${AUTHD_ROOT}/jwt_key -S data
user: kanband
consumes: auth
%file data
name: storage directory
creation-command: mkdir -p data && chown kanband:kanband data

View File

@ -1,11 +0,0 @@
name: kanband
command: kanband -k /srv/%{ENVIRONMENT}/jwt_key -S /srv/%{ENVIRONMENT}/kanban
user: kanban
environment-variables:
- LD_LIBRARY_PATH=/usr/local/lib
consumes:
- token: auth
checks:
- name: storage directory creation
directory: /srv/%{ENVIRONMENT}/kanban
command: mkdir -p /srv/%{ENVIRONMENT}/kanban && chown kanban:kanban /srv/%{ENVIRONMENT}/kanban

View File

@ -1,9 +1,6 @@
command: nginx -c ${SERVICE_ROOT}/nginx.conf
command: nginx -c nginx.conf
consumes: http?
provides: www, http
ports: http=80, https=443
%directory ${SERVICE_ROOT}/
name: working directory
%configuration nginx.conf ${SERVICE_ROOT}/nginx.conf
%configuration nginx.conf

View File

@ -1,32 +1,28 @@
name: postgresql
user: postgres
command: postgres -D ${SERVICE_ROOT} -k /tmp/postgresql-${ENVIRONMENT}
command: postgres -D ${SERVICE_ROOT}/db -k /tmp/postgresql-${ENVIRONMENT}
#stop-command: kill -HUP ${PID}
environment-variables:
- PGROOT=${SERVICE_ROOT}
provides: postgresql
ports: postgresql
%pre-start
name: database directory creation
unless-directory: ${SERVICE_ROOT}
command: mkdir -p ${SERVICE_ROOT} && chown postgres:postgres ${SERVICE_ROOT}
#%file db
# name: database directory
# command: mkdir -p db && chown postgres:postgres db
%pre-start
%file db
name: database creation
unless-file: ${SERVICE_ROOT}/base
command: su - postgres -c "initdb --locale en_US.UTF-8 -D '${SERVICE_ROOT}'" && rm ${SERVICE_ROOT}/postgresql.conf
creation-command: mkdir db && chown postgres:postgres db && su - postgres -c "initdb --locale en_US.UTF-8 -D '${SERVICE_ROOT}/db'" && rm db/postgresql.conf
#export-command: FIXME
%pre-start
name: database configuration
# once this file is created, there is no need to perform the command
unless-file: ${SERVICE_ROOT}/postgresql.conf
%configuration db/postgresql.conf
# gen-config inherits its parameters from the environment
command: gen-config postgresql.conf ${SERVICE_ROOT}/postgresql.conf && chown postgres:postgres ${SERVICE_ROOT}/postgresql.conf
creation-command: gen-config postgresql.conf ${SERVICE_ROOT}/postgresql.conf && chown postgres:postgres ${SERVICE_ROOT}/postgresql.conf
%pre-start
%file /tmp/${SERVICE_NAME}-${ENVIRONMENT}
name: sockets directory
unless-directory: /tmp/postgresql-${ENVIRONMENT}
unless-directory: /tmp/${SERVICE_NAME}-${ENVIRONMENT}
# FIXME: impose permissions
command: mkdir -p /tmp/postgresql-${ENVIRONMENT} && chown postgres:postgres /tmp/postgresql-${ENVIRONMENT}
command: mkdir -p /tmp/${SERVICE_NAME}-${ENVIRONMENT} && chown postgres:postgres /tmp/${SERVICE_NAME}-${ENVIRONMENT}
# FIXME: add postgresql-pre-start-db-dir around here

View File

@ -2,5 +2,6 @@ command: none
consumes: www
requires-domain: true
%directory ${SERVICE_ROOT}/
%file www
name: data directory
creation-command: mkdir www

View File

@ -8,7 +8,7 @@ class Environment
getter name : String
getter type : Type = Type::Prefix
getter pre_start_hooks = Array(ServiceDefinition::Hook).new
getter files = Array(ServiceDefinition::FileDefinition).new
# The place well put services data and configuration.
@root : String?
@ -20,12 +20,14 @@ class Environment
def initialize(@name, type = "prefix")
@type = Type.parse type
@pre_start_hooks = Array(ServiceDefinition::Hook).new
@files = Array(ServiceDefinition::FileDefinition).new
# FIXME: Should this *really* be here?
@pre_start_hooks << ServiceDefinition::Hook.new "Creating data directory",
"mkdir -p /srv/${ENVIRONMENT} && chmod a+rwt /srv/${ENVIRONMENT}",
unless_directory: "/srv/${ENVIRONMENT}"
# FIXME: $ENVIRONMENT_ROOT
@files << ServiceDefinition::FileDefinition.new "/srv/${ENVIRONMENT}",
"environment root",
creation_command: "mkdir -p /srv/${ENVIRONMENT} && chmod a+rwt /srv/${ENVIRONMENT}",
deletion_command: "rmdir /srv/${ENVIRONMENT}"
end
def initialize(@name, specs : SpecParser)
@ -36,7 +38,7 @@ class Environment
end
specs.sections.select(&.name.==("check")).each do |check|
@pre_start_hooks << ServiceDefinition::Hook.new check
@files << ServiceDefinition::FileDefinition.new check
end
end

View File

@ -193,6 +193,7 @@ class Service
private def build_environment
env = {} of String => String
env["SERVICE_NAME"] = name
env["SERVICE_ROOT"] = root
env["SERVICE_ID"] = full_id
env["ENVIRONMENT"] = @environment.name
@ -240,32 +241,33 @@ class Service
end
end
def pre_start_hooks
@environment.pre_start_hooks + @reference.pre_start_hooks
def files
@environment.files + @reference.files
end
def start(pid_dir : String, log_dir : String)
pre_start_hooks.each do |hook|
FileUtils.mkdir_p root
files.each do |file|
run_hook = false
hook.unless_file.try do |file|
file = evaluate file
run_hook = true if ! File.exists? file
next unless creation_command = file.creation_command
path = evaluate file.file_path
if path[0] != '/'
path = "#{root}/#{path}"
end
hook.unless_directory.try do |directory|
directory = evaluate directory
run_hook = true if ! Dir.exists? directory
end
run_hook = (!File.exists? path) || file.is_configuration?
unless run_hook
next
end
next unless run_hook
puts " - #{hook.name}"
puts " - Creating #{file.name}"
child = Process.fork do
Process.exec "sh", ["-c", hook.command],
Dir.cd root
Process.exec "sh", ["-c", creation_command],
output: Process::Redirect::Inherit,
error: Process::Redirect::Inherit,
env: build_environment

View File

@ -19,21 +19,40 @@ class ServiceDefinition
def initialize(@token)
end
end
struct Hook
struct FileDefinition
getter name : String
getter command : String
getter unless_directory : String?
getter unless_file : String?
getter file_path : String
def initialize(@name, @command, @unless_file = nil, @unless_directory = nil)
getter creation_command : String?
getter deletion_command : String?
getter export_command : String?
@configuration = false
def initialize(@file_path, @name = @file_path,
@creation_command = nil,
@deletion_command = nil,
@export_command = nil,
@configuration = false)
end
def initialize(section : SpecParser::Section)
@name = section.content["name"].as_s
@command = section.content["command"].as_s
@file_path = section.options[0]
@name = section.content["name"]?.try(&.as_s) || @file_path
@unless_directory = section.content["unless-directory"]?
@creation_command = section.content["creation-command"]?
.try &.as_s
@unless_file = section.content["unless-file"]?.try &.as_s
@deletion_command = section.content["deletion-command"]?
.try &.as_s
@export_command = section.content["export-command"]?
.try &.as_s
#@unless_directory = section.content["unless-directory"]?
# .try &.as_s
#@unless_file = section.content["unless-file"]?.try &.as_s
end
def is_configuration?
@configuration
end
end
@ -59,7 +78,7 @@ class ServiceDefinition
getter provides : String?
getter consumes : Array(Consumes)
getter environment_variables : Array(String)
getter pre_start_hooks : Array(Hook)
getter files : Array(FileDefinition)
getter provides : Array(Provides)
getter port_definitions : Array(PortDefinition)
getter non_runnable : Bool
@ -92,39 +111,39 @@ class ServiceDefinition
STDERR.puts "warning: definition '#{@name}' has a 'requires-domain' entry with an invalid value"
end
@pre_start_hooks = Array(Hook).new
@files = Array(FileDefinition).new
sections.each do |section|
case section.name
when "pre-start"
@pre_start_hooks << Hook.new section
when "directory"
directory = section.options[0]?
name = section.content["name"]?.try &.as_s
if directory.nil?
STDERR.puts "warning: (#{@name}) %directory was not provided a path"
next
end
pre_start_hooks << Hook.new (name || "directory: #{directory}"),
"mkdir -p \"#{directory}\"",
unless_directory: directory
when "file", "directory"
@files << FileDefinition.new section
when "configuration"
options = section.options[0].split /[ \t]/
template = options[0]?
target = options[1]?
name = section.content["name"]?.try &.as_s
if template.nil? || target.nil?
STDERR.puts "warning: (#{@name}) %configuration received less than 2 options"
if template.nil?
STDERR.puts "warning: (#{@name}) %configuration wasnt provided a target."
next
end
pre_start_hooks << Hook.new (name || "configuration file: #{template}"),
"gen-config \"#{template}\" \"#{target}\"",
unless_file: target
target = options[1]?
unless target
target = template
template = File.basename target
end
files << FileDefinition.new target, (name || target),
creation_command: "gen-config \"#{template}\" \"#{target}\"",
deletion_command: "rm \"#{target}\"",
configuration: true
when "database"
options = section.options[0].split /[ \t]/
type = options[0]? || section.content["type"]?.try &.as_s
# FIXME: %database is not currently implemented.
end
end
end