{% set parameter_dict = dict(default_parameter_dict, **parameter_dict) %} {% set additional_frontend = parameter_dict['additional-frontend-guid'] %} [buildout] extends = ${monitor-template:rendered} theia-environment-parts = tasks.json slapos-repository runner-link settings.json request-script-template theia-parts = frontend-reload promises parts = monitor-base $${:theia-parts} $${:theia-environment-parts} publish-connection-parameter eggs-directory = ${buildout:eggs-directory} develop-eggs-directory = ${buildout:develop-eggs-directory} offline = true [publish-connection-parameter] <= monitor-publish recipe = slapos.cookbook:publish url = $${remote-frontend:connection-secure_access} {% if additional_frontend %} additional-url = $${remote-additional-frontend:connection-secure_access} {% endif %} username = $${frontend-instance-password:username} password = $${frontend-instance-password:passwd} backend-url = $${frontend-instance:url} [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 runner = $${:srv}/runner project = $${:srv}/project 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/ # Promises # -------- [promises] recipe = instance-promises = $${theia-listen-promise:name} $${frontend-listen-promise:name} $${remote-frontend-url-available-promise:name} {% if additional_frontend %} $${remote-additional-frontend-url-available-promise:name} {% endif %} $${slapos-standalone-listen-promise:name} $${slapos-autorun-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} [remote-frontend-url-available-promise] <= monitor-promise-base module = check_url_available name = $${:_buildout_section_name_}.py config-url = $${remote-frontend:connection-secure_access} config-check-secure = 1 {% if additional_frontend %} [remote-additional-frontend-url-available-promise] <= monitor-promise-base module = check_url_available name = $${:_buildout_section_name_}.py config-url = $${remote-additional-frontend:connection-secure_access} config-check-secure = 1 {% endif %} [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} [slapos-autorun-promise] <= monitor-promise-base module = check_service_state # XXX promise plugins can not contain "slapos" in their names name = autorun-state-promise.py config-service = $${slapos-autorun:service-name} config-expect = $${slapos-autorun:autorun} # Remote Caddy Frontend # --------------------- [remote-frontend-base] <= slap-connection recipe = slapos.cookbook:requestoptional 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 [remote-frontend] <= remote-frontend-base name = {{ parameter_dict['frontend-name'] }} software-url = {{ parameter_dict['frontend-sr'] }} software-type = {{ parameter_dict['frontend-sr-type'] }} sla-instance_guid = {{ parameter_dict['frontend-guid'] }} {% if additional_frontend %} [remote-additional-frontend] <= remote-frontend-base name = {{ parameter_dict['additional-frontend-name'] }} software-url = {{ parameter_dict['additional-frontend-sr'] }} software-type = {{ parameter_dict['additional-frontend-sr-type'] }} sla-instance_guid = {{ parameter_dict['additional-frontend-guid'] }} {% endif %} # Local Caddy Frontend # -------------------- [frontend-instance-password] recipe = slapos.cookbook:generate.password username = admin bytes = 12 [frontend-instance-port] recipe = slapos.cookbook:free_port minimum = 3000 maximum = 3100 ip = {{ ipv6_random }} [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 = $${frontend-instance-port:ip} hostname = [$${:ip}] port = $${frontend-instance-port:port} [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 -p $${:location} ln -sf ${source-code-pro-fonts:location} $${:location}/source-code-pro ln -sf ${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 = cp --remove-destination ${logo.png:output} $${:full-path} 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_} # Common Environment # ------------------ [common-environment] recipe = slapos.recipe.template:jinja2 rendered = $${directory:bin}/$${:_buildout_section_name_} mode = 0700 template = inline: #!/bin/sh export HOME=$${buildout:directory} export PATH=${python-language-server:location}/bin:${java-jdk:location}/bin:${cli-utilities:PATH}:$HOME/.cargo/bin:$PATH # Theia Backend # ------------- [theia-service-port] recipe = slapos.cookbook:free_port minimum = 3500 maximum = 3600 ip = {{ ipv4_random }} [theia-service] recipe = slapos.recipe.template:jinja2 rendered = $${directory:bin}/$${:_buildout_section_name_} mode = 0700 template = inline:#!/bin/sh {{ "{% raw %}" }} {% raw %} export THEIA_WEBVIEW_EXTERNAL_ENDPOINT='{{hostname}}' {% endraw %} {{ "{% 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 TMP=$${directory:tmp} export TEMP=$TMP export LC_ALL=C.UTF-8 export TERMINFO=${ncurses:location}/lib/terminfo/ export EDITOR="${theia-open:rendered} --wait" export THEIA_DEFAULT_PLUGINS="local-dir:${theia-plugins:location}" . $${common-environment:rendered} exec ${theia-wrapper:rendered} "$@" ip = $${theia-service-port:ip} port = $${theia-service-port:port} 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 = {{ 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 = {% raw %} inline:#!{{ bash }} SHELL=$BASH # when running interactively, activate slapos configuration and reset GIT_EXEC_PATH to workaround https://github.com/eclipse-theia/theia/issues/7555 if [ $# = 0 ]; then . {{ activate }} unset GIT_EXEC_PATH set -- --rcfile {{ bashrc }} # otherwise, assume this shell is running task and add an artificial delay to workaround https://github.com/eclipse-theia/theia/issues/2961 else sleep 1 fi exec "$SHELL" "$@" {% endraw %} context = raw bash ${bash:location}/bin/bash key activate slapos-standalone-activate:rendered key bashrc theia-bashrc:rendered [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 # ----------------- [slapos-standalone-port] recipe = slapos.cookbook:free_port minimum = 4000 maximum = 4100 ip = {{ ipv4_random }} [slapos-standalone-config] ipv4 = {{ ipv4_random }} ipv6 = {{ ipv6_random }} port = $${slapos-standalone-port:port} slapos-configuration = $${directory:runner}/etc/slapos.cfg computer-id = slaprunner [slapos-standalone-activate] recipe = slapos.recipe.template:jinja2 rendered = $${directory:bin}/$${:_buildout_section_name_} template = inline: 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 . $${common-environment:rendered} #XXX find out where the extra nodejs in theia's PATH comes from export PATH=${nodejs:location}/bin/:$PATH . $${slapos-standalone-activate:rendered} exec ${slapos-standalone:script-path} \ $${directory:runner} \ $${slapos-standalone-config:ipv4} \ $${slapos-standalone-config:ipv6} \ $${slapos-standalone-config:port} \ $${slapos-standalone-config:computer-id} \ {% if parameter_dict.get('embedded-sr') %} \ --sr='{{ parameter_dict['embedded-sr'] }}' \ --srtype='{{ parameter_dict['embedded-sr-type'] }}' \ --srparams='$${embedded-instance-parameters:rendered}' \ {% endif %} \ $${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} # Slapos Standalone Autoprocessing # -------------------------------- [slapos-autorun] recipe = plone.recipe.command command = case $${:autorun} in ( running ) ${buildout:bin-directory}/supervisorctl -c $${:supervisor-conf} start $${:service-name};; ( stopped ) ${buildout:bin-directory}/supervisorctl -c $${:supervisor-conf} stop $${:service-name};; esac update-command = $${:command} service-name = slapos-node-auto supervisor-conf = $${directory:runner}/etc/supervisord.conf autorun = {{ parameter_dict['autorun'] }} # Theia Local Environment Setup # ----------------------------- [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 } } ] } [slapos-repository] recipe = slapos.recipe.build:gitclone repository = https://lab.nexedi.com/nexedi/slapos.git location = $${directory:project}/slapos branch = 1.0 develop = true git-executable = ${git:location}/bin/git [settings.json] recipe = slapos.recipe.template:jinja2 rendered = $${directory:dot-theia}$${:_buildout_section_name_} once = $${:rendered} template = inline: { "files.watcherExclude": { "**/.eggs/**": true, "**/.env/**": true, "**/.git/**": true, "**/node_modules/**": true, "$${directory:runner}/**":true, "$${directory:project}/runner/**":true } } [runner-link] recipe = slapos.cookbook:symbolic.link target-directory = $${directory:project} link-binary = $${directory:runner} {% if parameter_dict.get('embedded-sr') -%} [embedded-instance-parameters] recipe = slapos.recipe.template:jinja2 rendered = $${directory:etc}/$${:_buildout_section_name_}.json template = inline:{{ parameter_dict['embedded-instance-parameters'] | indent(2) }} {%- endif %} [request-script-template] recipe = slapos.recipe.template:jinja2 rendered = $${directory:project}/$${:_buildout_section_name_}.sh mode = 0700 template = inline:#!/bin/sh # This template is generated automatically, copy it in order to supply and request. # Any manual change to this file may be lost. software_name=html5as-base #replace the software name writen in ~/srv/project/slapos/software/ software_release_uri=~/srv/project/slapos/software/$software_name/software.cfg # slapos supply is used to add the software to the software list to be supplied to a node. slapos supply $software_release_uri slaprunner # slapos request the allocation of an instance to the master. # slapos request also gets status and parameters of the instance if it has any # (slapos request is meant to be run multiple time until you get the status). slapos request $software_name'_1' $software_release_uri