Commit 1a5df533 authored by Jérome Perrin's avatar Jérome Perrin

slaprunner: support shared parts

If slaprunner itself was installed on a slapos which had shared parts
enabled, these shared parts will also be used while installing softwares
inside the slaprunner.

Because ${buildout:shared-part-list} is a multi line property, it was
not possible to generate .cfg files (which needs special handling of
multi-line strings) directly from software buildout (where jinja
templates are not yet available), so we use a trick of generating a
plain text file in software and parsing it during instance.
parent 95c05120
......@@ -18,7 +18,7 @@ md5sum = c44a7481bb85e3258128afe3fcf23f44
filename = instance-runner.cfg
md5sum = 1d5edfc1e9637d81746afbff316b9b1c
md5sum = 19475112d4dccee1263798c67fd2351b
filename = template/
......@@ -50,7 +50,7 @@ md5sum = 525e37ea8b2acf6209869999b15071a6
filename = template/
md5sum = 035e027e9cb9bbdca0509ac895fc4696
md5sum = da113b3e3e7bac9cc215fede7c4911a5
filename =
......@@ -79,3 +79,7 @@ md5sum = 2451072826a9ad9425d62c9e9c7f6284
filename = template/
md5sum = 75aab99c995ca841f93fc77fc9116c37
filename = template/
md5sum = 3203c9ad0b30d3ee39a809a067efff8d
\ No newline at end of file
......@@ -150,6 +150,7 @@ project = $${:home}/project
public = $${:home}/public
software-root = {{ slapparameter_dict.get('software-root', '$${:home}/software') }}
instance-root = $${:home}/instance
shared-root = $${:home}/shared
project-test = $${:test}/project
software-test = $${:test}/software
instance-test = $${:test}/instance
......@@ -169,6 +170,8 @@ working-directory = $${runnerdirectory:home}
project-directory = $${runnerdirectory:project}
instance_root = $${runnerdirectory:instance-root}
software_root = $${runnerdirectory:software-root}
shared_root = $${runnerdirectory:shared-root}
buildout-shared-part-list-dump = ${template-buildout-shared-part-list:output}
pidfile-software = $${directory:run}/
pidfile-instance = $${directory:run}/
ssh_client = ${openssh:location}/bin/ssh
......@@ -707,6 +710,8 @@ rendered = $${slaprunner:slapos.cfg}
mode = 700
context =
section slaprunner slaprunner
import codecs codecs
raw buildout_shared_part_list_dump ${template-buildout-shared-part-list:output}
recipe = slapos.recipe.template:jinja2
......@@ -27,6 +27,10 @@ extends =
# make sure shared-part-list is available, even for old versions
# of slapos who do not set that.
shared-part-list =
# stacks are listed from most generic to most specific,
# to avoid versioning issues
......@@ -131,6 +135,10 @@ filename =
< = template-download-base
filename =
< = template-base
output = ${buildout:directory}/buildout-shared-part-list
recipe = zc.recipe.egg
eggs =
\ No newline at end of file
software_root = {{ slaprunner['software_root'] }}
instance_root = {{ slaprunner['instance_root'] }}
shared_part_list =
{#- buildout_shared_part_list_dump is ${buildout:shared-part-list} rendered as a
template during software step.
Because it can contain new lines, it's not possible to use it directly when generating
buildout config files from buildout, because the newlines don't get indented and cause
the instance buildout to be invalid ini file.
So we had to dump it in a simple text file and now we parse again that text file. -#}
{%- for line in %}
{{ line.strip() }}
{%- endfor %}
{{ slaprunner['shared_root'] }}
master_url = http://{{ slaprunner['ipv4'] }}:{{ slaprunner['proxy_port'] }}
computer_id = slaprunner
maximal_delay = 0
......@@ -30,8 +30,10 @@ import paramiko
import contextlib
import base64
import hashlib
import subprocess
from six.moves.urllib.parse import urlparse
from six.moves.urllib.parse import quote
from six.moves.configparser import ConfigParser
from slapos.recipe.librecipe import generateHashFromFiles
from slapos.testing.testcase import makeModuleSetUpAndTestCaseClass
......@@ -122,6 +124,45 @@ class TestSSH(SlaprunnerTestCase):
class TestSlapOS(SlaprunnerTestCase):
def test_slapos_command(self):
# in ~/bin/slapos there is a wrapper setting configuration to use slapos from
# the web runner.
  • Sometimes this fail, like this result.

    It seems we have a symlinks recipe that might also create a non-working ~/bin/slapos, maybe that what's happen sometimes, but I think order of part installation is deterministic in buildout, so probably not.

    It would help to include ~/bin in the snapshots ( @luke do you see any problem with that ?)

  • No problem at all, lets add it. Also <partition>etc/service and <partition>/etc/run would be helpful to inspect instance correctness.

  • I was thinking we already include etc/ recursively already, I added comments in slapos.core!150 (merged) ( there might be a better place ... )à

  • I copied the result of a test run now that we snapshot bin https://[2001:67c:1254:69:17f7::e700]:9080/cqg-dT8bi3wf9K/test.TestSlapOS.test_slapos_command/

    and https://[2001:67c:1254:69:17f7::e700]:9080/cqg-dT8bi3wf9K/test.TestSlapOS.test_slapos_command/inst/s0/bin/slapos is

    # run slapos command inside slaprunner with appropriate config
    export SLAPOS_CONFIGURATION=${SLAPOS_CONFIGURATION-"/srv/slapgrid/slappart8/srv/testnode/cqg/inst/test0-0/tmp/inst/s0/etc//slapos.cfg"}
    exec /srv/slapgrid/slappart8/srv/testnode/cqg/inst/test0-0/tmp/soft/45aa16ca91ed41af9bbe5f76f153d85d/bin/slapos "$@"
    $ grep slapos.core /srv/slapgrid/slappart8/srv/testnode/cqg/inst/test0-0/tmp/soft/45aa16ca91ed41af9bbe5f76f153d85d/bin/slapos

    in inst/s0/etc/supervisord.conf we are also using the same slapos.

    So the problem might not be (unless what I initially thought) that a wrong version of slapos.core is used, but just that the database is not created yet. I tried locally and when there's no proxy.db that's the error we get.

    Isn't a promise missing ? I feel the instance should not be considered as ready before slapproxy is started and also before the database is initialized.

  • In slaprunner, the proxy is started when slaprunner is started :

    There is a promise "slaprunner", which tests in reality that nginx listens on the IPv6:Port. Nginx communicates with gunicorn (which spawns the slaprunner in workers) through a socket. So It must be possible that the promise succeeds ("hey ! Something is bound to the port !"), but gunicorn is not started yet.

    We could add a test_port promise for slapproxy, as it listens on a private IPv4.

  • Ah thanks. If I understand correctly, the database is initialized on the first HTTP request but the promise just checks that a TCP connection can be made. Maybe just switching to a promise making an actual HTTP request would be enough...

Please register or sign in to reply
proxy_show_output = subprocess.check_output(
os.path.join(self.computer_partition_root_path, 'bin', 'slapos'),
self.assertIn('slaprunner', proxy_show_output)
def test_shared_part_list(self):
# this slapos used shared_part_list
cfg_parser = ConfigParser()
with open(os.path.join(self.computer_partition_root_path,
'slapos.cfg')) as f:
shared_part_list = cfg_parser.get('slapos', 'shared_part_list').splitlines()
# web runner own shared parts. Note that there is intentionnaly a double
# slash in this path, because slaprunner has double slash in paths since
# early releases, including for the path of slapos repository that will be
# used to develop and install software. If we fix this duplication, then
# the URL of installed software will be different and it will get a different
# hash and be reinstalled. To prevent this, we keep that // between srv and runner.
# shared parts from outer slapos
outer_shared_part_list = os.getenv('SLAPOS_TEST_SHARED_PART_LIST',
for outer_shared_part in outer_shared_part_list:
self.assertIn(outer_shared_part, shared_part_list)
class ServicesTestCase(SlaprunnerTestCase):
def test_hashes(self):
hash_files = [
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment