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
parent
e0e81d8581
commit
87929be026
|
@ -1,22 +1,17 @@
|
||||||
name: authd
|
name: authd
|
||||||
command: authd -K ${SERVICE_ROOT}/jwt_key -u ${SERVICE_ROOT}/passwd -g ${SERVICE_ROOT}/group
|
command: authd -K jwt_key -u passwd -g group
|
||||||
environment-variables:
|
|
||||||
- LD_LIBRARY_PATH=/usr/local/lib
|
|
||||||
user: authd
|
user: authd
|
||||||
provides: auth
|
provides: auth
|
||||||
%pre-start
|
|
||||||
name: Creating IPC directory
|
%file /run/ipc
|
||||||
unless-directory: /run/ipc
|
name: IPC directory
|
||||||
command: install -d -m6777 /run/ipc
|
command: install -d -m6777 /run/ipc
|
||||||
%pre-start
|
%file jwt_key
|
||||||
name: Creating JWT key
|
name: JWT key
|
||||||
unless-file: ${SERVICE_ROOT}/jwt_key
|
creation-command: head -c 64 /dev/urandom | base64 > jwt_key
|
||||||
command: head -c 64 /dev/urandom | base64 > ${SERVICE_ROOT}/jwt_key
|
%file passwd
|
||||||
%pre-start
|
|
||||||
name: passwd file
|
name: passwd file
|
||||||
unless-file: ${SERVICE_ROOT}/passwd
|
command: touch passwd
|
||||||
command: touch ${SERVICE_ROOT}/passwd
|
%file group
|
||||||
%pre-start
|
|
||||||
name: group file
|
name: group file
|
||||||
unless-file: ${SERVICE_ROOT}/group
|
command: touch group
|
||||||
command: touch ${SERVICE_ROOT}/group
|
|
||||||
|
|
|
@ -1,15 +1,23 @@
|
||||||
command: gitea -C . -w . -c ./custom/conf/app.ini
|
command: gitea -C . -w . -c gitea.cfg
|
||||||
consumes: postgresql, http?
|
consumes: postgresql, http?
|
||||||
requires-domain: true
|
requires-domain: true
|
||||||
ports: http
|
ports: http
|
||||||
|
|
||||||
%directory ${SERVICE_ROOT}/custom/conf
|
#%directory ${SERVICE_ROOT}/custom/conf
|
||||||
name: working directory
|
# name: working directory
|
||||||
|
# configuration: true
|
||||||
|
|
||||||
%configuration gitea.cfg ${SERVICE_ROOT}/custom/conf/app.ini
|
%file db-is-setup
|
||||||
|
name: postgresql database
|
||||||
%pre-start
|
|
||||||
name: gitea database creation
|
|
||||||
# 'command' is run only if this directory doesn't exist
|
# 'command' is run only if this directory doesn't exist
|
||||||
unless-file: ${SERVICE_ROOT}/db_is_setup
|
creation-command: pg_create_user.sh create_user_and_db && touch db-is-setup
|
||||||
command: pg_create_user.sh create_user_and_db && touch ${SERVICE_ROOT}/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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
|
@ -1,9 +1,6 @@
|
||||||
command: nginx -c ${SERVICE_ROOT}/nginx.conf
|
command: nginx -c nginx.conf
|
||||||
consumes: http?
|
consumes: http?
|
||||||
provides: www, http
|
provides: www, http
|
||||||
ports: http=80, https=443
|
ports: http=80, https=443
|
||||||
|
|
||||||
%directory ${SERVICE_ROOT}/
|
%configuration nginx.conf
|
||||||
name: working directory
|
|
||||||
|
|
||||||
%configuration nginx.conf ${SERVICE_ROOT}/nginx.conf
|
|
||||||
|
|
|
@ -1,32 +1,28 @@
|
||||||
name: postgresql
|
name: postgresql
|
||||||
user: postgres
|
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}
|
#stop-command: kill -HUP ${PID}
|
||||||
environment-variables:
|
environment-variables:
|
||||||
- PGROOT=${SERVICE_ROOT}
|
- PGROOT=${SERVICE_ROOT}
|
||||||
provides: postgresql
|
provides: postgresql
|
||||||
ports: postgresql
|
ports: postgresql
|
||||||
|
|
||||||
%pre-start
|
#%file db
|
||||||
name: database directory creation
|
# name: database directory
|
||||||
unless-directory: ${SERVICE_ROOT}
|
# command: mkdir -p db && chown postgres:postgres db
|
||||||
command: mkdir -p ${SERVICE_ROOT} && chown postgres:postgres ${SERVICE_ROOT}
|
|
||||||
|
|
||||||
%pre-start
|
%file db
|
||||||
name: database creation
|
name: database creation
|
||||||
unless-file: ${SERVICE_ROOT}/base
|
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
|
||||||
command: su - postgres -c "initdb --locale en_US.UTF-8 -D '${SERVICE_ROOT}'" && rm ${SERVICE_ROOT}/postgresql.conf
|
#export-command: FIXME
|
||||||
|
|
||||||
%pre-start
|
%configuration db/postgresql.conf
|
||||||
name: database configuration
|
|
||||||
# once this file is created, there is no need to perform the command
|
|
||||||
unless-file: ${SERVICE_ROOT}/postgresql.conf
|
|
||||||
# gen-config inherits its parameters from the environment
|
# 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
|
name: sockets directory
|
||||||
unless-directory: /tmp/postgresql-${ENVIRONMENT}
|
unless-directory: /tmp/${SERVICE_NAME}-${ENVIRONMENT}
|
||||||
# FIXME: impose permissions
|
# 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
|
# FIXME: add postgresql-pre-start-db-dir around here
|
||||||
|
|
|
@ -2,5 +2,6 @@ command: none
|
||||||
consumes: www
|
consumes: www
|
||||||
requires-domain: true
|
requires-domain: true
|
||||||
|
|
||||||
%directory ${SERVICE_ROOT}/
|
%file www
|
||||||
name: data directory
|
name: data directory
|
||||||
|
creation-command: mkdir www
|
||||||
|
|
|
@ -8,7 +8,7 @@ class Environment
|
||||||
|
|
||||||
getter name : String
|
getter name : String
|
||||||
getter type : Type = Type::Prefix
|
getter type : Type = Type::Prefix
|
||||||
getter pre_start_hooks = Array(ServiceDefinition::Hook).new
|
getter files = Array(ServiceDefinition::FileDefinition).new
|
||||||
|
|
||||||
# The place we’ll put services’ data and configuration.
|
# The place we’ll put services’ data and configuration.
|
||||||
@root : String?
|
@root : String?
|
||||||
|
@ -20,12 +20,14 @@ class Environment
|
||||||
def initialize(@name, type = "prefix")
|
def initialize(@name, type = "prefix")
|
||||||
@type = Type.parse type
|
@type = Type.parse type
|
||||||
|
|
||||||
@pre_start_hooks = Array(ServiceDefinition::Hook).new
|
@files = Array(ServiceDefinition::FileDefinition).new
|
||||||
|
|
||||||
# FIXME: Should this *really* be here?
|
# FIXME: Should this *really* be here?
|
||||||
@pre_start_hooks << ServiceDefinition::Hook.new "Creating data directory",
|
# FIXME: $ENVIRONMENT_ROOT
|
||||||
"mkdir -p /srv/${ENVIRONMENT} && chmod a+rwt /srv/${ENVIRONMENT}",
|
@files << ServiceDefinition::FileDefinition.new "/srv/${ENVIRONMENT}",
|
||||||
unless_directory: "/srv/${ENVIRONMENT}"
|
"environment root",
|
||||||
|
creation_command: "mkdir -p /srv/${ENVIRONMENT} && chmod a+rwt /srv/${ENVIRONMENT}",
|
||||||
|
deletion_command: "rmdir /srv/${ENVIRONMENT}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(@name, specs : SpecParser)
|
def initialize(@name, specs : SpecParser)
|
||||||
|
@ -36,7 +38,7 @@ class Environment
|
||||||
end
|
end
|
||||||
|
|
||||||
specs.sections.select(&.name.==("check")).each do |check|
|
specs.sections.select(&.name.==("check")).each do |check|
|
||||||
@pre_start_hooks << ServiceDefinition::Hook.new check
|
@files << ServiceDefinition::FileDefinition.new check
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -193,6 +193,7 @@ class Service
|
||||||
private def build_environment
|
private def build_environment
|
||||||
env = {} of String => String
|
env = {} of String => String
|
||||||
|
|
||||||
|
env["SERVICE_NAME"] = name
|
||||||
env["SERVICE_ROOT"] = root
|
env["SERVICE_ROOT"] = root
|
||||||
env["SERVICE_ID"] = full_id
|
env["SERVICE_ID"] = full_id
|
||||||
env["ENVIRONMENT"] = @environment.name
|
env["ENVIRONMENT"] = @environment.name
|
||||||
|
@ -240,32 +241,33 @@ class Service
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def pre_start_hooks
|
def files
|
||||||
@environment.pre_start_hooks + @reference.pre_start_hooks
|
@environment.files + @reference.files
|
||||||
end
|
end
|
||||||
|
|
||||||
def start(pid_dir : String, log_dir : String)
|
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
|
run_hook = false
|
||||||
|
|
||||||
hook.unless_file.try do |file|
|
next unless creation_command = file.creation_command
|
||||||
file = evaluate file
|
|
||||||
run_hook = true if ! File.exists? file
|
path = evaluate file.file_path
|
||||||
|
if path[0] != '/'
|
||||||
|
path = "#{root}/#{path}"
|
||||||
end
|
end
|
||||||
|
|
||||||
hook.unless_directory.try do |directory|
|
run_hook = (!File.exists? path) || file.is_configuration?
|
||||||
directory = evaluate directory
|
|
||||||
run_hook = true if ! Dir.exists? directory
|
|
||||||
end
|
|
||||||
|
|
||||||
unless run_hook
|
next unless run_hook
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
puts " - #{hook.name}"
|
puts " - Creating #{file.name}"
|
||||||
|
|
||||||
child = Process.fork do
|
child = Process.fork do
|
||||||
Process.exec "sh", ["-c", hook.command],
|
Dir.cd root
|
||||||
|
|
||||||
|
Process.exec "sh", ["-c", creation_command],
|
||||||
output: Process::Redirect::Inherit,
|
output: Process::Redirect::Inherit,
|
||||||
error: Process::Redirect::Inherit,
|
error: Process::Redirect::Inherit,
|
||||||
env: build_environment
|
env: build_environment
|
||||||
|
|
|
@ -19,21 +19,40 @@ class ServiceDefinition
|
||||||
def initialize(@token)
|
def initialize(@token)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
struct Hook
|
struct FileDefinition
|
||||||
getter name : String
|
getter name : String
|
||||||
getter command : String
|
getter file_path : String
|
||||||
getter unless_directory : String?
|
|
||||||
getter unless_file : 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
|
end
|
||||||
def initialize(section : SpecParser::Section)
|
def initialize(section : SpecParser::Section)
|
||||||
@name = section.content["name"].as_s
|
@file_path = section.options[0]
|
||||||
@command = section.content["command"].as_s
|
@name = section.content["name"]?.try(&.as_s) || @file_path
|
||||||
|
|
||||||
@unless_directory = section.content["unless-directory"]?
|
@creation_command = section.content["creation-command"]?
|
||||||
.try &.as_s
|
.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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -59,7 +78,7 @@ class ServiceDefinition
|
||||||
getter provides : String?
|
getter provides : String?
|
||||||
getter consumes : Array(Consumes)
|
getter consumes : Array(Consumes)
|
||||||
getter environment_variables : Array(String)
|
getter environment_variables : Array(String)
|
||||||
getter pre_start_hooks : Array(Hook)
|
getter files : Array(FileDefinition)
|
||||||
getter provides : Array(Provides)
|
getter provides : Array(Provides)
|
||||||
getter port_definitions : Array(PortDefinition)
|
getter port_definitions : Array(PortDefinition)
|
||||||
getter non_runnable : Bool
|
getter non_runnable : Bool
|
||||||
|
@ -92,39 +111,39 @@ class ServiceDefinition
|
||||||
STDERR.puts "warning: definition '#{@name}' has a 'requires-domain' entry with an invalid value"
|
STDERR.puts "warning: definition '#{@name}' has a 'requires-domain' entry with an invalid value"
|
||||||
end
|
end
|
||||||
|
|
||||||
@pre_start_hooks = Array(Hook).new
|
@files = Array(FileDefinition).new
|
||||||
|
|
||||||
sections.each do |section|
|
sections.each do |section|
|
||||||
case section.name
|
case section.name
|
||||||
when "pre-start"
|
when "file", "directory"
|
||||||
@pre_start_hooks << Hook.new section
|
@files << FileDefinition.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 "configuration"
|
when "configuration"
|
||||||
options = section.options[0].split /[ \t]/
|
options = section.options[0].split /[ \t]/
|
||||||
|
|
||||||
template = options[0]?
|
template = options[0]?
|
||||||
target = options[1]?
|
|
||||||
name = section.content["name"]?.try &.as_s
|
name = section.content["name"]?.try &.as_s
|
||||||
|
|
||||||
if template.nil? || target.nil?
|
if template.nil?
|
||||||
STDERR.puts "warning: (#{@name}) %configuration received less than 2 options"
|
STDERR.puts "warning: (#{@name}) %configuration wasn’t provided a target."
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
pre_start_hooks << Hook.new (name || "configuration file: #{template}"),
|
target = options[1]?
|
||||||
"gen-config \"#{template}\" \"#{target}\"",
|
unless target
|
||||||
unless_file: 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
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue