Commit 1d07b08a authored by Xavier Thompson's avatar Xavier Thompson

software/theia: Fix embedded instance request

Avoid restarting standalone when embedded instance parameters change.
parent eda8ad27
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
[instance-theia] [instance-theia]
_update_hash_filename_ = instance-theia.cfg.jinja.in _update_hash_filename_ = instance-theia.cfg.jinja.in
md5sum = 4df9f0d76a134a8abec9060a0c1be50b md5sum = 9d41697aef6c92ff1dcf633c675e6202
[instance] [instance]
_update_hash_filename_ = instance.cfg.in _update_hash_filename_ = instance.cfg.in
......
{% set parameter_dict = dict(default_parameter_dict, **parameter_dict) %} {%- set parameter_dict = dict(default_parameter_dict, **parameter_dict) %}
{% set additional_frontend = parameter_dict['additional-frontend-guid'] %} {%- set additional_frontend = parameter_dict['additional-frontend-guid'] %}
{%- set embedded_computer_id = 'slaprunner' %}
{%- set embedded_sr = parameter_dict['embedded-sr'] %}
{%- set embedded_sr_type = parameter_dict['embedded-sr-type'] %}
{%- set embedded_instance_parameters = parameter_dict['embedded-instance-parameters'] %}
{%- if embedded_sr %}
{%- if embedded_sr.startswith('~/') %}
{%- set embedded_sr = os_module.path.join(partition_root_path, embedded_sr[2:]) %}
{%- set embedded_sr = os_module.path.normpath(embedded_sr) %}
{%- endif %}
{%- endif -%}
[buildout] [buildout]
extends = extends =
...@@ -51,6 +61,7 @@ dot-theia = $${:home}/.theia/ ...@@ -51,6 +61,7 @@ dot-theia = $${:home}/.theia/
pidfiles = $${:var}/run pidfiles = $${:var}/run
services = $${:etc}/service services = $${:etc}/service
scripts = $${:etc}/run
runner = $${:srv}/runner runner = $${:srv}/runner
backup = $${:srv}/backup/theia backup = $${:srv}/backup/theia
project = $${:srv}/project project = $${:srv}/project
...@@ -92,12 +103,15 @@ instance-promises = ...@@ -92,12 +103,15 @@ instance-promises =
$${frontend-listen-promise:name} $${frontend-listen-promise:name}
$${frontend-authentication-promise:name} $${frontend-authentication-promise:name}
$${remote-frontend-url-available-promise:name} $${remote-frontend-url-available-promise:name}
{% if additional_frontend %} {%- if additional_frontend %}
$${remote-additional-frontend-url-available-promise:name} $${remote-additional-frontend-url-available-promise:name}
{% endif %} {%- endif %}
$${slapos-standalone-listen-promise:name} $${slapos-standalone-listen-promise:name}
$${slapos-standalone-ready-promise:name} $${slapos-standalone-ready-promise:name}
$${slapos-autorun-promise:name} $${slapos-autorun-promise:name}
{%- if embedded_sr -%}
$${embedded-instance-promise:name}
{%- endif %}
[theia-listen-promise] [theia-listen-promise]
<= monitor-promise-base <= monitor-promise-base
...@@ -161,6 +175,15 @@ config-service = $${slapos-autorun:service-name} ...@@ -161,6 +175,15 @@ config-service = $${slapos-autorun:service-name}
config-expect = $${slapos-autorun:autorun} config-expect = $${slapos-autorun:autorun}
config-run-directory = $${directory:runner}/var/run config-run-directory = $${directory:runner}/var/run
{% if embedded_sr -%}
[embedded-instance-promise]
<= monitor-promise-base
depends = $${request-embedded-instance:recipe}
promise = check_command_execute
name = embedded-instance-promise.py
config-command = $${directory:runner}/bin/slapos service info embedded_instance
{%- endif %}
# Remote Caddy Frontend # Remote Caddy Frontend
# --------------------- # ---------------------
...@@ -447,38 +470,45 @@ command = ...@@ -447,38 +470,45 @@ command =
# Embedded Instance # Embedded Instance
# ----------------- # -----------------
{%- set embedded_sr = parameter_dict['embedded-sr'] %} {% if embedded_sr -%}
{%- set embedded_sr_type = parameter_dict['embedded-sr-type'] %} [request-embedded-instance]
{%- set embedded_instance_parameters = parameter_dict['embedded-instance-parameters'] %} recipe = slapos.recipe.build
{%- if embedded_sr %} location = $${directory:project}/request_embedded.sh
{%- if embedded_sr.startswith('~/') %} install =
{%- set embedded_sr = os_module.path.join(partition_root_path, embedded_sr[2:]) %} import os
{%- set embedded_sr = os_module.path.normpath(embedded_sr) %} import socket
{%- endif %} with open(options['location'], 'w') as f:
[request-embedded-instance-script] f.write(options['content'])
recipe = slapos.recipe.template:jinja2 os.fchmod(f.fileno(), 0o700)
rendered = $${directory:project}/request_embedded.sh s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
mode = 0700 try:
template = s.connect("\0$${slapos-standalone-config:abstract-socket-path}")
inline:#!/bin/sh s.sendall(b"r")
except socket.error:
pass
finally:
s.close()
content =
#!/bin/sh
set -e
slapos supply {{ embedded_sr }} slaprunner slapos supply {{ embedded_sr }} slaprunner
slapos request "embedded_instance" {{ embedded_sr }} slapos request "embedded_instance" {{ embedded_sr }}
{%- if embedded_sr_type %} --type {{ embedded_sr_type }} {%- endif %} {%- if embedded_sr_type %} --type {{ embedded_sr_type }} {%- endif %}
{%- if embedded_instance_parameters %} --parameters-file $${embedded-instance-parameters:rendered} {%- if embedded_instance_parameters %} --parameters-file $${embedded-instance-parameters:location}
[embedded-instance-parameters] [embedded-instance-parameters]
recipe = slapos.recipe.template:jinja2 recipe = slapos.recipe.build
rendered = $${directory:project}/$${:_buildout_section_name_}.json location = $${directory:project}/$${:_buildout_section_name_}.json
template = install =
inline:{{ embedded_instance_parameters | indent(2) }} with open(options['location'], 'w') as f:
f.write(options['content'])
content =
{{ embedded_instance_parameters | indent(2) }}
{%- endif %} {%- endif %}
{%- endif %} {%- endif %}
{%- set embedded_digest = str(embedded_sr) + str(embedded_sr_type) + str(embedded_instance_parameters) %}
{%- set embedded_digest_hash = hashlib_module.md5(embedded_digest.encode()).hexdigest() %}
# SlapOS Standalone # SlapOS Standalone
# ----------------- # -----------------
...@@ -495,8 +525,8 @@ ipv6 = {{ ipv6_random }} ...@@ -495,8 +525,8 @@ ipv6 = {{ ipv6_random }}
port = $${slapos-standalone-port:port} port = $${slapos-standalone-port:port}
local-software-release-root = $${directory:home} local-software-release-root = $${directory:home}
slapos-configuration = $${directory:runner}/etc/slapos.cfg slapos-configuration = $${directory:runner}/etc/slapos.cfg
computer-id = slaprunner computer-id = {{ embedded_computer_id }}
abstract-socket-path = $${directory:home}/standalone-{{ embedded_digest_hash[:16] }} abstract-socket-path = $${directory:home}/standalone-ready
[slapos-standalone-activate] [slapos-standalone-activate]
recipe = slapos.recipe.template:jinja2 recipe = slapos.recipe.template:jinja2
...@@ -525,9 +555,6 @@ template = ...@@ -525,9 +555,6 @@ template =
import slapos.slap.standalone import slapos.slap.standalone
# Include this hash, so that if it changes the standalone service will be restarted
# {{ embedded_digest_hash }}
shared_parts = """{{ '''${buildout:shared-part-list}''' | indent(2) }}""" shared_parts = """{{ '''${buildout:shared-part-list}''' | indent(2) }}"""
shared_part_list = [x.strip() for x in shared_parts.splitlines() if x.strip()] shared_part_list = [x.strip() for x in shared_parts.splitlines() if x.strip()]
partition_forward_configuration = ( partition_forward_configuration = (
...@@ -571,8 +598,7 @@ template = ...@@ -571,8 +598,7 @@ template =
standalone.waitForInstance(max_retry=0) standalone.waitForInstance(max_retry=0)
except slapos.slap.standalone.SlapOSNodeCommandError as e: except slapos.slap.standalone.SlapOSNodeCommandError as e:
print("Error instanciating: {}".format(e)) print("Error instanciating: {}".format(e))
{%- if embedded_sr %} # Compatibility
# Compatibility layer
try: try:
for cp in standalone.computer.getComputerPartitionList(): for cp in standalone.computer.getComputerPartitionList():
if cp.getInstanceParameterDict().get("instance_title") == "Embedded Instance": if cp.getInstanceParameterDict().get("instance_title") == "Embedded Instance":
...@@ -580,23 +606,31 @@ template = ...@@ -580,23 +606,31 @@ template =
cp.rename(new_name="embedded_instance") cp.rename(new_name="embedded_instance")
break break
except Exception: except Exception:
print("Exception in compatibility layer, printing and moving on") pass
traceback.print_exc() # Open a socket to signal standalone is ready
# Run request script
print("Running SlapOS script $${request-embedded-instance-script:rendered}")
slapos_env = {
'PATH': os.path.dirname(standalone._slapos_bin),
'SLAPOS_CONFIGURATION': standalone._slapos_config,
'SLAPOS_CLIENT_CONFIGURATION': standalone._slapos_config
}
subprocess.call(("$${request-embedded-instance-script:rendered}",), env=slapos_env)
{%- endif %}
s = socket.socket(socket.AF_UNIX) s = socket.socket(socket.AF_UNIX)
s.bind("\0$${slapos-standalone-config:abstract-socket-path}") s.bind("\0$${slapos-standalone-config:abstract-socket-path}")
s.listen(5) s.listen(5)
print("Standalone SlapOS ready") print("Standalone SlapOS ready")
# Wait for connections and call embedded instance script when notified
request_script_path = "$${directory:project}/request_embedded.sh"
slapos_env = {
"PATH": "$${directory:runner}/bin",
"SLAPOS_CONFIGURATION": "$${slapos-standalone-config:slapos-configuration}",
"SLAPOS_CLIENT_CONFIGURATION": "$SLAPOS_CONFIGURATION",
}
do_request = True
while True: while True:
s.accept()[0].close() if do_request:
try:
subprocess.call((request_script_path,), env=slapos_env)
except OSError:
pass
try:
conn, _ = s.accept()
do_request = conn.recv(1) == b"r"
finally:
conn.close()
finally: finally:
print("Stopping standalone subsystem") print("Stopping standalone subsystem")
standalone.stop() standalone.stop()
......
...@@ -251,60 +251,80 @@ class ReRequestMixin(object): ...@@ -251,60 +251,80 @@ class ReRequestMixin(object):
class TestTheiaWithSR(TheiaTestCase, ReRequestMixin): class TestTheiaWithSR(TheiaTestCase, ReRequestMixin):
sr_url = '~/bogus/software.cfg' old_instance_name = 'Embedded Instance'
sr_type = 'bogus_type' instance_name = 'embedded_instance'
instance_parameters = '{\n"bogus_param": "bogus_value",\n"bogus_param2": "bogus_value2"\n}'
def proxy_show(self, slapos): def check_output(self, *commands):
return subprocess.check_output((slapos, 'proxy', 'show'), universal_newlines=True) return subprocess.check_output(commands, universal_newlines=True)
def test(self):
slapos = self._getSlapos()
home = self.computer_partition_root_path
# Check that no request script was generated
request_script = os.path.join(home, 'srv', 'project', 'request_embedded.sh')
self.assertFalse(os.path.exists(request_script))
# Manually request old-name 'Embedded Instance'
old_instance_name = "Embedded Instance"
subprocess.check_call((slapos, 'request', old_instance_name, 'bogus_url'))
self.assertIn(old_instance_name, self.proxy_show(slapos))
def update(self, sr_url, sr_type, sr_parameters):
# Update Theia instance parameters # Update Theia instance parameters
embedded_request_parameters = { embedded_request_parameters = {
'embedded-sr': self.sr_url, 'embedded-sr': sr_url,
'embedded-sr-type': self.sr_type, 'embedded-sr-type': sr_type,
'embedded-instance-parameters': self.instance_parameters 'embedded-instance-parameters': json.dumps(sr_parameters, indent=1)
} }
self.rerequest(embedded_request_parameters) self.rerequest(embedded_request_parameters)
self.reinstantiate() self.reinstantiate()
slapos = self._getSlapos()
home = self.computer_partition_root_path
if sr_url.startswith('~/'):
sr_url = os.path.join(home, sr_url[2:])
# Check that request script was generated
request_script = os.path.join(home, 'srv', 'project', 'request_embedded.sh')
self.assertTrue(os.path.exists(request_script))
# Check that embedded instance was requested # Check that embedded instance was requested
instance_name = "embedded_instance" instance_name = self.instance_name
info = self.proxy_show(slapos) info = self.check_output(slapos, 'proxy', 'show')
try:
self.assertIn(instance_name, info) self.assertIn(instance_name, info)
except AssertionError:
for filename in os.listdir(home): # Check embedded instance parameters
if 'standalone' in filename and '.log' in filename: self.assertIsNotNone(re.search(r"%s\s+slaprunner\s+available" % (sr_url,), info), info)
filepath = os.path.join(home, filename) self.assertIsNotNone(re.search(r"%s\s+%s\s+%s" % (sr_url, sr_type, instance_name), info), info)
with open(filepath) as f: service_info = self.check_output(slapos, 'service', 'info', instance_name)
print("Contents of filepath: " + filepath) for k, v in sr_parameters.items():
print(f.read()) self.assertIn("'%s': '%s'" % (k, v), service_info)
raise
# Check that old-name instance was renamed # Check that old-name instance was renamed
self.assertNotIn(old_instance_name, info) self.assertNotIn(self.old_instance_name, self.check_output(slapos, 'proxy', 'show'))
# Check embedded instance parameters def test(self):
bogus_sr = os.path.join(home, self.sr_url[2:]) slapos = self._getSlapos()
home = self.computer_partition_root_path
self.assertIsNotNone(re.search(r"%s\s+slaprunner\s+available" % (bogus_sr,), info), info) # Check that no request script was generated
self.assertIsNotNone(re.search(r"%s\s+%s\s+%s" % (bogus_sr, self.sr_type, instance_name), info), info) request_script = os.path.join(home, 'srv', 'project', 'request_embedded.sh')
self.assertFalse(os.path.exists(request_script))
service_info = subprocess.check_output((slapos, 'service', 'info', instance_name), universal_newlines=True) # Manually request old-name 'Embedded Instance'
self.assertIn("{'bogus_param': 'bogus_value', 'bogus_param2': 'bogus_value2'}", service_info) subprocess.check_call((slapos, 'request', self.old_instance_name, 'bogus_url'))
self.assertIn(self.old_instance_name, self.check_output(slapos, 'proxy', 'show'))
# Stop standalone so that it will be restarted to test compatibility
with self.slap.instance_supervisor_rpc as supervisor:
for process_info in supervisor.getAllProcessInfo():
service_name = process_info['name']
if 'standalone' in service_name:
supervisor.stopProcess('%s:%s' % (process_info['group'], service_name))
break
# Update Theia instance parameters and verify results
self.update(
sr_url = '~/bogus/software.cfg',
sr_type = 'bogus',
sr_parameters = {'key1': 'value1', 'key2': 'value2'}
)
# Update again without restarting stadalone service and verify results
self.update(
sr_url = '/bogus/software.cfg',
sr_type = 'bogus2',
sr_parameters = {'key1': 'value1', 'key3': 'value3'}
)
class TestTheiaFrontend(TheiaTestCase): class TestTheiaFrontend(TheiaTestCase):
......
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