#!{{ python_for_standalone }} import contextlib import glob import json import logging import os import re import signal import socket import subprocess import sys import time import slapos.slap.standalone from slapos.util import unicode2str logging.basicConfig(format="[%(asctime)s] %(message)s", level=logging.DEBUG) REQUEST_SCRIPT_RAW_TEMPLATE = """{{ request_script_template }}""" {% raw -%} REQUEST_SCRIPT_TEMPLATE = re.sub( r"{{\s*(\w+)\s*}}", r"{\1}", REQUEST_SCRIPT_RAW_TEMPLATE, ) {%- endraw %} REQUEST_SCRIPT_HEADER_TEXT = """ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # This script is generated once by your Theia's SlapOS Standalone service. # # # # It was used to pre-request an instance inside Theia's embedded SlapOS # # according to the preconfiguration parameters given to your Theia. # # # # It will not be overwritten or recreated. # # It will not be launched automatically. # # # # You can edit it and run it to modify your embedded instance. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # """.strip() def signal_handler(signum, frame): logging.info("Signal %d received", signum) sys.exit() @contextlib.contextmanager def setupStandalone(): {%- if forward_frontend_requests != "disabled" %} partition_forward_configuration = ( slapos.slap.standalone.PartitionForwardAsPartitionConfiguration( master_url={{ repr(slap_connection['server-url']) }}, computer={{ repr(slap_connection['computer-id']) }}, partition={{ repr(slap_connection['partition-id']) }}, cert={{ repr(slap_connection['cert-file']) }}, key={{ repr(slap_connection['key-file']) }}, software_release_list=( 'http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg', ), ), ) {%- else %} partition_forward_configuration = () {%- endif %} shared_parts = {{ repr(shared_part_list) }} shared_part_list = [x.strip() for x in shared_parts.splitlines() if x.strip()] standalone = slapos.slap.standalone.StandaloneSlapOS( {{ repr(slapos_standalone_config['base-directory']) }}, {{ repr(slapos_standalone_config['ipv4']) }}, {{ slapos_standalone_config['port'] }}, computer_id={{ repr(slapos_standalone_config['computer-id']) }}, shared_part_list=shared_part_list, software_root={{ repr(slapos_standalone_config['software-root']) }}, instance_root={{ repr(slapos_standalone_config['instance-root']) }}, partition_forward_configuration=partition_forward_configuration, slapos_bin={{ repr(slapos_standalone_config['slapos-bin']) }}, local_software_release_root={{ repr(slapos_standalone_config['local-software-release-root']) }}, ) standalone.start() try: partition_count = 20 logging.info("Standalone SlapOS: Formatting %d partitions", partition_count) standalone.format(partition_count, {{ repr(slapos_standalone_config['ipv4']) }}, {{ repr(slapos_standalone_config['ipv6']) }}) logging.info("Standalone SlapOS for computer `%s` started", {{ repr(slapos_standalone_config['computer-id']) }}) # Run instance at least once, to start the supervisor managing instances. try: standalone.waitForInstance(max_retry=0) except slapos.slap.standalone.SlapOSNodeCommandError as e: logging.info("Error instanciating: {}".format(e)) yield standalone finally: logging.info("Stopping standalone subsystem") standalone.stop() logging.info("Exiting") def parseEmbeddedInstanceConfig(config_json_file): with open(config_json_file) as f: try: config = json.load(f) except json.JSONDecodeError: logging.error("%s is not valid JSON", config_json_file) raise assert(isinstance(config, dict)) if config: software_url = unicode2str(config['software-url']) software_type = config.get('software-type') instance_parameters = config.get('instance-parameters') assert(isinstance(software_url, str)) if software_type: software_type = unicode2str(software_type) assert(isinstance(software_type, str)) if instance_parameters: assert(isinstance(instance_parameters, dict)) return software_url, software_type, instance_parameters def createRequestScript(software_url, software_type, instance_parameters): # Generate request script request_script_path = {{ repr(request_script_path) }} parameters_file_path = {{ repr(parameters_file_path) }} request_options = "embedded_instance " + software_url if software_type: request_options += ' --type ' + software_type if instance_parameters: with open(parameters_file_path, 'w') as f: json.dump(instance_parameters, f) request_options += ' --parameters-file ' + parameters_file_path with open(request_script_path, 'w') as f: f.write(REQUEST_SCRIPT_TEMPLATE.format( header_text = REQUEST_SCRIPT_HEADER_TEXT, software_url = software_url, request_options = request_options, )) f.write("\n") os.fchmod(f.fileno(), 0o755) return request_script_path def main(): signal.signal(signal.SIGTERM, signal_handler) with setupStandalone() as standalone: config_json_file = {{ repr(embedded_instance_config) }} done_file = config_json_file + '.done' if not os.path.exists(done_file): with open(done_file, 'w'): pass try: config = parseEmbeddedInstanceConfig(config_json_file) except Exception: logging.info("Failed to parse embedded instance config", exc_info=True) config = None if config: try: request_script_path = createRequestScript(*config) logging.info("Calling %s", request_script_path) exitcode = subprocess.call((request_script_path,), env={ 'HOME': {{ repr(home_path) }}, 'PATH': os.path.dirname(standalone._slapos_bin), 'SLAPOS_CONFIGURATION': standalone._slapos_config, 'SLAPOS_CLIENT_CONFIGURATION': standalone._slapos_config, }) with open({{ repr(embedded_request_exitcode_file) }}, 'w') as f: f.write(str(exitcode)) except Exception: logging.info("Failed to request embedded instance", exc_info=True) s = socket.socket(socket.AF_UNIX) s.bind({{ repr('\0' + slapos_standalone_config['abstract-socket-path']) }}) s.listen(5) logging.info("Standalone SlapOS ready") while True: s.accept()[0].close() main()