[buildout] parts = monitor-base promises frontend-reload tasks.json publish-connection-parameter extends = ${monitor-template:rendered} eggs-directory = ${buildout:eggs-directory} develop-eggs-directory = ${buildout:develop-eggs-directory} offline = true [frontend-instance-password] recipe = slapos.cookbook:generate.password username = node bytes = 12 [frontend-instance-certificate] recipe = plone.recipe.command command = if [ ! -e $${:key-file} ] then ${openssl-output:openssl} req -x509 -nodes -days 3650 \ -subj "/C=AA/ST=X/L=X/O=Dis/CN=$${:common-name}" \ -newkey rsa:1024 -keyout $${:key-file} \ -out $${:cert-file} fi update-command = $${:command} key-file = $${directory:etc}/$${:_buildout_section_name_}.key cert-file = $${directory:etc}/$${:_buildout_section_name_}.crt common-name = $${frontend-instance-config:ip} location = $${:key-file} $${:cert-file} [frontend-instance-config] recipe = slapos.recipe.template:jinja2 rendered = $${directory:etc}/$${:_buildout_section_name_} template = inline: :$${:port} { bind $${:ip} tls $${frontend-instance-certificate:cert-file} $${frontend-instance-certificate:key-file} log stdout errors stderr gzip # because caddy does not support upgrade http2 to websocket # https://tools.ietf.org/html/rfc8441 tls { alpn http/1.1 } root $${directory:frontend-static} browse proxy / $${theia-instance:base-url} { except $${frontend-instance-fonts:folder-name} $${frontend-instance-slapos.css:folder-name} public $${favicon.ico:filename} $${frontend-instance-logo:filename} } proxy /services $${theia-instance:base-url} { websocket } proxy /file-upload $${theia-instance:base-url} { websocket } basicauth $${frontend-instance-password:username} $${frontend-instance-password:passwd} { realm "Theia" / } } ip = $${instance-parameter:ipv6-random} hostname = [$${:ip}] port = 3001 [frontend-instance] recipe = slapos.cookbook:wrapper wrapper-path = $${directory:services}/$${:_buildout_section_name_} command-line = ${caddy:output} -conf $${frontend-instance-config:rendered} -pidfile $${:pidfile} ip = $${frontend-instance-config:ip} hostname = $${frontend-instance-config:hostname} port = $${frontend-instance-config:port} pidfile = $${directory:pidfiles}/$${:_buildout_section_name_}.pid url = https://$${:hostname}:$${:port}/ [frontend-instance-fonts] ; XXX caddy 1 does not seem to serve different folders at different locations ; so we link fonts in static folder recipe = plone.recipe.command location = $${directory:frontend-static}/$${:folder-name} folder-name = fonts command = mkdir $${:location} ln -s ${source-code-pro-fonts:location} $${:location}/source-code-pro ln -s ${jetbrains-mono-fonts:location} $${:location}/jetbrains-mono stop-on-error = true [frontend-instance-logo] recipe = plone.recipe.command filename = logo.png full-path = $${directory:frontend-static}/$${:filename} command = if [ ! -e $${:full-path} ] then ln -s ${logo.png:output} $${:full-path} fi stop-on-error = true [frontend-instance-slapos.css] recipe = slapos.recipe.template:jinja2 template = ${slapos.css.in:output} rendered = $${directory:frontend-static}/$${:folder-name}/slapos.css folder-name = css context = key logo_image frontend-instance-logo:filename [frontend-reload] recipe = slapos.cookbook:wrapper wrapper-path = $${directory:services}/$${:_buildout_section_name_} command-line = ${bash:location}/bin/bash -c "kill -s USR1 $$(${coreutils:location}/bin/cat $${frontend-instance:pidfile}) \ && ${coreutils:location}/bin/sleep infinity" hash-files = $${frontend-instance-config:rendered} $${frontend-instance:wrapper-path} wait-for-files = $${frontend-instance:pidfile} [favicon.ico] # generate a pseudo random favicon, different for each instance name. recipe = slapos.recipe.build install = import hashlib, shutil buildout_offline = self.buildout['buildout']['offline'] self.buildout['buildout']['offline'] = 'false' try: gravatar_url = "https://www.gravatar.com/avatar/" + hashlib.md5( '''$${slap-configuration:root-instance-title}''' ).hexdigest() + "?s=256&d=retro" shutil.copy(self.download(gravatar_url), '''$${:location}''') except Exception: # Because installation should work offline, if we can't download a favicon, # just ignore this step. self.logger.exception("Error while downloading favicon, using empty one") open('''$${:location}''', 'w').close() finally: self.buildout['buildout']['offline'] = buildout_offline location = $${directory:frontend-static}/$${:filename} filename = $${:_buildout_section_name_} [tasks.json] recipe = slapos.recipe.template:jinja2 rendered = $${directory:dot-theia}/tasks.json template = inline: { // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "label": "slapos node software", "detail": "Build all software supplied to the node", "type": "shell", "command": "${buildout:bin-directory}/slapos", "args": [ "node", "software", // debug mode can be enabled by commenting out this line: // "--buildout-debug", "--all" ], "options": { "env": { "SLAPOS_CONFIGURATION": "$${slapos-standalone-config:slapos-configuration}", "GIT_EXEC_PATH": "" } }, "group": { "kind": "build", "isDefault": true }, "problemMatcher": [] }, { "label": "slapos node instance", "detail": "Create all instances requested on the node", "type": "shell", "command": "${buildout:bin-directory}/slapos", "args": [ "node", "instance", // debug mode can be enabled by commenting out this line: // "--buildout-debug", "--all" ], "options": { "env": { "SLAPOS_CONFIGURATION": "$${slapos-standalone-config:slapos-configuration}", "GIT_EXEC_PATH": "" } }, "problemMatcher": [], "group": { "kind": "build", "isDefault": true } } ] } [theia-service] recipe = slapos.recipe.template:jinja2 rendered = $${directory:bin}/$${:_buildout_section_name_} mode = 0700 template = inline:#!/bin/sh {% raw %} export THEIA_WEBVIEW_EXTERNAL_ENDPOINT='{{hostname}}' {% endraw %} export THEIA_OPEN_EDITOR_TOKEN=$(${openssl:location}/bin/openssl rand -hex 32) export THEIA_URL=$${:base-url} export THEIA_SHELL=$${theia-shell:rendered} export HOME=$${buildout:directory} export TMP=$${directory:tmp} export TEMP=$TMP export LC_ALL=C.UTF-8 export TERMINFO=${ncurses:location}/lib/terminfo/ export EDITOR="${python-language-server:location}/bin/python -m theia_open --wait" exec ${theia-wrapper:rendered} $@ ip = $${instance-parameter:ipv4-random} port = 3000 base-url = http://$${:ip}:$${:port}/ [theia-instance] recipe = slapos.cookbook:wrapper wrapper-path = $${directory:services}/$${:_buildout_section_name_} command-line = $${theia-service:rendered} --hostname=$${:hostname} --port=$${:port} $${directory:project} hash-existing-files = ${yarn.lock:output} ${theia-wrapper:rendered} ip = $${instance-parameter:ipv4-random} hostname = $${:ip} port = $${theia-service:port} base-url = $${theia-service:base-url} [theia-shell] recipe = slapos.recipe.template:jinja2 rendered = $${directory:bin}/$${:_buildout_section_name_} mode = 0700 template = inline: #!${python:location}/bin/python import os import sys import time args = sys.argv[1:] # when running interactively, activate slapos configuration and reset GIT_EXEC_PATH to workaround https://github.com/eclipse-theia/theia/issues/7555 if not args: args = ["-c", ". $${slapos-standalone-activate:rendered} && exec env GIT_EXEC_PATH= ${bash:location}/bin/bash --rcfile $${theia-bashrc:rendered}", ] # otherwise, assume this shell is running task and add an artificial delay to workaround https://github.com/eclipse-theia/theia/issues/2961 else: time.sleep(1) os.execv('${bash:location}/bin/bash', ['${bash:location}/bin/bash'] + args) [theia-bashrc] recipe = slapos.recipe.template:jinja2 rendered = $${directory:etc}/$${:_buildout_section_name_} template = inline: # enable bash completion . ${bash-completion:location}/etc/profile.d/bash_completion.sh # source user's .bashrc [ -f ~/.bashrc ] && . ~/.bashrc depends = $${shell-setup-completion:recipe} [shell-setup-completion] recipe = plone.recipe.command stop-on-error = true command = ${buildout:bin-directory}/slapos complete > $${directory:bash-completions}/slapos ${buildout:bin-directory}/slapos complete --shell fish > $${directory:fish-completions}/slapos.fish [slapos-standalone-config] ipv4 = $${instance-parameter:ipv4-random} ipv6 = $${instance-parameter:ipv6-random} port = 4000 slapos-configuration = $${directory:slapos}/etc/slapos.cfg computer-id = slaprunner [slapos-standalone-activate] recipe = slapos.recipe.template:jinja2 rendered = $${directory:bin}/$${:_buildout_section_name_} mode = 0700 template = inline:#!/bin/sh export PATH=${buildout:bin-directory}:$PATH export SLAPOS_CONFIGURATION=$${slapos-standalone-config:slapos-configuration} export SLAPOS_CLIENT_CONFIGURATION=$SLAPOS_CONFIGURATION echo 'Standalone SlapOS for computer `$${slapos-standalone-config:computer-id}` activated' [slapos-standalone] recipe = slapos.recipe.template:jinja2 rendered = $${directory:bin}/$${:_buildout_section_name_} mode = 0700 template = inline:#!/bin/sh export PATH=${buildout:bin-directory}:$PATH exec ${slapos-standalone:script-path} \ $${directory:slapos} \ $${slapos-standalone-config:ipv4} \ $${slapos-standalone-config:ipv6} \ $${slapos-standalone-config:port} \ $${slapos-standalone-config:computer-id} \ $${slap-connection:server-url} \ $${slap-connection:computer-id} \ $${slap-connection:partition-id} \ --key='$${slap-connection:key-file}' \ --cert='$${slap-connection:cert-file}' [slapos-standalone-instance] recipe = slapos.cookbook:wrapper wrapper-path = $${directory:services}/$${:_buildout_section_name_} command-line = $${slapos-standalone:rendered} hash-files = $${slapos-standalone:rendered} hostname = $${slapos-standalone-config:ipv4} port = $${slapos-standalone-config:port} [promises] recipe = instance-promises = $${theia-listen-promise:name} $${frontend-listen-promise:name} $${apache-frontend-url-available-promise:name} $${slapos-standalone-listen-promise:name} [theia-listen-promise] <= monitor-promise-base module = check_port_listening name = $${:_buildout_section_name_}.py config-hostname = $${theia-instance:ip} config-port = $${theia-instance:port} [frontend-listen-promise] <= monitor-promise-base module = check_port_listening name = $${:_buildout_section_name_}.py config-hostname = $${frontend-instance:ip} config-port = $${frontend-instance:port} [apache-frontend-url-available-promise] <= monitor-promise-base module = check_url_available name = $${:_buildout_section_name_}.py config-url = $${apache-frontend:connection-secure_access} config-check-secure = 1 [slapos-standalone-listen-promise] <= monitor-promise-base module = check_port_listening # XXX promise plugins can not contain "slapos" in their names name = standalone-listen-promise.py config-hostname = $${slapos-standalone-instance:hostname} config-port = $${slapos-standalone-instance:port} [apache-frontend] <= slap-connection recipe = slapos.cookbook:requestoptional name = Theia Frontend # XXX We have hardcoded SR URL here. software-url = http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg slave = true config-url = $${frontend-instance:url} config-https-only = true config-type = websocket config-websocket-path-list = /services /file-upload return = domain secure_access [publish-connection-parameter] recipe = slapos.cookbook:publish url = $${apache-frontend:connection-secure_access} username = $${frontend-instance-password:username} password = $${frontend-instance-password:passwd} [instance-parameter] recipe = slapos.cookbook:slapconfiguration computer = $${slap-connection:computer-id} partition = $${slap-connection:partition-id} url = $${slap-connection:server-url} key = $${slap-connection:key-file} cert = $${slap-connection:cert-file} [directory] recipe = slapos.cookbook:mkdirectory etc = $${buildout:directory}/etc var = $${buildout:directory}/var srv = $${buildout:directory}/srv bin = $${buildout:directory}/bin tmp = $${buildout:directory}/tmp dot-theia = $${buildout:directory}/.theia/ pidfiles = $${:var}/run services = $${:etc}/service project = $${:srv}/project slapos = $${:srv}/slapos frontend-static = $${:srv}/frontend-static frontend-static-public = $${:frontend-static}/public frontend-static-css = $${:frontend-static}/css bash-completions = $${buildout:directory}/.local/share/bash-completion/completions/ fish-completions = $${buildout:directory}/.config/fish/completions/