diff options
Diffstat (limited to 'items/svc_s6rc.py')
| -rw-r--r-- | items/svc_s6rc.py | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/items/svc_s6rc.py b/items/svc_s6rc.py new file mode 100644 index 0000000..429c04a --- /dev/null +++ b/items/svc_s6rc.py @@ -0,0 +1,140 @@ +from shlex import quote +from pathlib import Path + +from bundlewrap.items import Item + +S6RC_ROOT = "/etc/s6-rc" + +def generate_request_path(template, service) -> str: + return Path(S6RC_ROOT, "generate", f"{template}.{service}").as_posix() + +def service_dependency_path(bundle, service) -> str: + return Path(S6RC_ROOT, "services", bundle, "contents.d", service).as_posix() + +def have_generate_template(node, template) -> bool: + template_path = Path(S6RC_ROOT, "templates", template) + try: + result = node.run( + f"test -d {quote(template_path.as_posix())}", + ) + return result.return_code == 0 + except: + return False + +def is_service_generated(node, template, service) -> bool: + try: + result = node.run( + f"test -f {quote(generate_request_path(template, service))}", + ) + return result.return_code == 0 + except: + return False + +def generate_service(node, template, service) -> bool: + """ + This doesn't really generate the service from the template. Rather it + requests the s6-rc-generate target to do it on boot. + """ + result = node.run( + f"touch {quote(generate_request_path(template, service))}", + ) + return result.return_code == 0 + +def is_service_registered(node, bundle, service) -> bool: + try: + result = node.run( + f"test -f {quote(service_dependency_path(bundle, service))}", + ) + return result.return_code == 0 + except: + return False + +def register_service(node, bundle, service) -> bool: + result = node.run( + f"touch {quote(service_dependency_path(bundle, service))}", + ) + return result.return_code == 0 + +def is_service_running(node, service) -> bool: + try: + result = node.run( + f"s6-rc -v2 -bt 30000 -a list | grep -q {quote(service)}", + ) + return result.return_code == 0 + except: + return False + +def run_service(node, service) -> bool: + result = node.run( + f"s6-rc -v2 -bt 30000 start {quote(service)}", + ) + return result.return_code == 0 + +class SvcS6RC(Item): + """ + A service managed by s6-rc. + """ + + BUNDLE_ATTRIBUTE_NAME = "svc_s6rc" + ITEM_ATTRIBUTES = { + "bundle": "default", + "generate": None, + "running": True, + } + ITEM_TYPE_NAME = "svc_s6rc" + REQUIRED_ATTRIBUTES = [] + + def __repr__(self): + return "<SvcS6RC name:{} running:{} bundle:{} generate:{}>".format( + self.name, + self.attributes["running"], + self.attributes["bundle"], + self.attributes["generate"], + ) + + def get_canned_actions(self): + return { + "stop": { + "command": f"s6-rc -v2 -b -t 30000 stop {quote(self.name)}", + "needed_by": {self.id}, + }, + "restart": { + "command": f"s6-rc -v2 -b -t 30000 stop {quote(self.name)} && s6-rc -v2 -b -t 30000 start {quote(self.name)}", + "needs": {self.id}, + }, + } + + def cdict(self): + return { + "running": self.attributes["running"], + "generated": self.attributes["generate"] != None, + "registered": self.attributes["bundle"] != None, + } + + def sdict(self): + return { + "running": is_service_running(self.node, self.name), + "generated": is_service_generated(self.node, + self.attributes["generate"], + self.name) if + self.attributes["generate"] != None else False, + "registered": is_service_registered(self.node,self.attributes["bundle"], self.name), + } + + def fix(self, status): + if status.correct: + return + + if self.attributes["generate"]: + if not have_generate_template(self.node, self.attributes["generate"]): + raise Exception(f"No template provides `{self.attributes["generate"]}`") + + if status.cdict["generated"] != status.sdict["generated"]: + generate_service(self.node, self.attributes["generate"], self.name) + + if status.cdict["registered"] != status.sdict["registered"]: + register_service(self.node, self.attributes["bundle"], self.name) + + if status.cdict["running"] != status.sdict["running"]: + run_service(self.node, self.name) + |